modal 1.1.5.dev66__py3-none-any.whl → 1.3.1.dev8__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 +4 -4
- modal/__main__.py +4 -29
- modal/_billing.py +84 -0
- modal/_clustered_functions.py +1 -3
- modal/_container_entrypoint.py +33 -208
- modal/_functions.py +171 -138
- modal/_grpc_client.py +191 -0
- modal/_ipython.py +16 -6
- modal/_load_context.py +106 -0
- modal/_object.py +72 -21
- modal/_output.py +12 -14
- modal/_partial_function.py +31 -4
- modal/_resolver.py +44 -57
- modal/_runtime/container_io_manager.py +30 -28
- modal/_runtime/container_io_manager.pyi +42 -44
- modal/_runtime/gpu_memory_snapshot.py +9 -7
- modal/_runtime/user_code_event_loop.py +80 -0
- modal/_runtime/user_code_imports.py +236 -10
- modal/_serialization.py +2 -1
- modal/_traceback.py +4 -13
- modal/_tunnel.py +16 -11
- modal/_tunnel.pyi +25 -3
- modal/_utils/async_utils.py +337 -10
- modal/_utils/auth_token_manager.py +1 -4
- modal/_utils/blob_utils.py +29 -22
- modal/_utils/function_utils.py +20 -21
- modal/_utils/grpc_testing.py +6 -3
- modal/_utils/grpc_utils.py +223 -64
- modal/_utils/mount_utils.py +26 -1
- modal/_utils/name_utils.py +2 -3
- modal/_utils/package_utils.py +0 -1
- modal/_utils/rand_pb_testing.py +8 -1
- modal/_utils/task_command_router_client.py +524 -0
- modal/_vendor/cloudpickle.py +144 -48
- modal/app.py +285 -105
- modal/app.pyi +216 -53
- modal/billing.py +5 -0
- modal/builder/2025.06.txt +6 -3
- modal/builder/PREVIEW.txt +2 -1
- modal/builder/base-images.json +4 -2
- modal/cli/_download.py +19 -3
- modal/cli/cluster.py +4 -2
- modal/cli/config.py +3 -1
- modal/cli/container.py +5 -4
- modal/cli/dict.py +5 -2
- modal/cli/entry_point.py +26 -2
- modal/cli/environment.py +2 -16
- modal/cli/launch.py +1 -76
- modal/cli/network_file_system.py +5 -20
- modal/cli/programs/run_jupyter.py +1 -1
- modal/cli/programs/vscode.py +1 -1
- modal/cli/queues.py +5 -4
- modal/cli/run.py +24 -204
- modal/cli/secret.py +1 -2
- modal/cli/shell.py +375 -0
- modal/cli/utils.py +1 -13
- modal/cli/volume.py +11 -17
- modal/client.py +16 -125
- modal/client.pyi +94 -144
- modal/cloud_bucket_mount.py +3 -1
- modal/cloud_bucket_mount.pyi +4 -0
- modal/cls.py +101 -64
- modal/cls.pyi +9 -8
- modal/config.py +21 -1
- modal/container_process.py +288 -12
- modal/container_process.pyi +99 -38
- modal/dict.py +72 -33
- modal/dict.pyi +88 -57
- modal/environments.py +16 -8
- modal/environments.pyi +6 -2
- modal/exception.py +154 -16
- modal/experimental/__init__.py +24 -53
- modal/experimental/flash.py +161 -74
- modal/experimental/flash.pyi +97 -49
- modal/file_io.py +50 -92
- modal/file_io.pyi +117 -89
- modal/functions.pyi +70 -87
- modal/image.py +82 -47
- modal/image.pyi +51 -30
- modal/io_streams.py +500 -149
- modal/io_streams.pyi +279 -189
- modal/mount.py +60 -46
- modal/mount.pyi +41 -17
- modal/network_file_system.py +19 -11
- modal/network_file_system.pyi +72 -39
- modal/object.pyi +114 -22
- modal/parallel_map.py +42 -44
- modal/parallel_map.pyi +9 -17
- modal/partial_function.pyi +4 -2
- modal/proxy.py +14 -6
- modal/proxy.pyi +10 -2
- modal/queue.py +45 -38
- modal/queue.pyi +88 -52
- modal/runner.py +96 -96
- modal/runner.pyi +44 -27
- modal/sandbox.py +225 -107
- modal/sandbox.pyi +226 -60
- modal/secret.py +58 -56
- modal/secret.pyi +28 -13
- modal/serving.py +7 -11
- modal/serving.pyi +7 -8
- modal/snapshot.py +29 -15
- modal/snapshot.pyi +18 -10
- modal/token_flow.py +1 -1
- modal/token_flow.pyi +4 -6
- modal/volume.py +102 -55
- modal/volume.pyi +125 -66
- {modal-1.1.5.dev66.dist-info → modal-1.3.1.dev8.dist-info}/METADATA +10 -9
- modal-1.3.1.dev8.dist-info/RECORD +189 -0
- modal_proto/api.proto +141 -70
- modal_proto/api_grpc.py +42 -26
- modal_proto/api_pb2.py +1123 -1103
- modal_proto/api_pb2.pyi +331 -83
- modal_proto/api_pb2_grpc.py +80 -48
- modal_proto/api_pb2_grpc.pyi +26 -18
- modal_proto/modal_api_grpc.py +175 -174
- modal_proto/task_command_router.proto +164 -0
- modal_proto/task_command_router_grpc.py +138 -0
- modal_proto/task_command_router_pb2.py +180 -0
- modal_proto/{sandbox_router_pb2.pyi → task_command_router_pb2.pyi} +148 -57
- modal_proto/task_command_router_pb2_grpc.py +272 -0
- modal_proto/task_command_router_pb2_grpc.pyi +100 -0
- modal_version/__init__.py +1 -1
- modal_version/__main__.py +1 -1
- modal/cli/programs/launch_instance_ssh.py +0 -94
- modal/cli/programs/run_marimo.py +0 -95
- modal-1.1.5.dev66.dist-info/RECORD +0 -191
- 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_proto/sandbox_router.proto +0 -125
- modal_proto/sandbox_router_grpc.py +0 -89
- modal_proto/sandbox_router_pb2.py +0 -128
- modal_proto/sandbox_router_pb2_grpc.py +0 -169
- modal_proto/sandbox_router_pb2_grpc.pyi +0 -63
- {modal-1.1.5.dev66.dist-info → modal-1.3.1.dev8.dist-info}/WHEEL +0 -0
- {modal-1.1.5.dev66.dist-info → modal-1.3.1.dev8.dist-info}/entry_points.txt +0 -0
- {modal-1.1.5.dev66.dist-info → modal-1.3.1.dev8.dist-info}/licenses/LICENSE +0 -0
- {modal-1.1.5.dev66.dist-info → modal-1.3.1.dev8.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
|
|
@@ -74,6 +75,42 @@ class _FunctionDecoratorType:
|
|
|
74
75
|
self, func: collections.abc.Callable[P, ReturnType]
|
|
75
76
|
) -> modal.functions.Function[P, ReturnType, ReturnType]: ...
|
|
76
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
|
+
|
|
77
114
|
class _App:
|
|
78
115
|
"""A Modal App is a group of functions and classes that are deployed together.
|
|
79
116
|
|
|
@@ -110,18 +147,16 @@ class _App:
|
|
|
110
147
|
_container_app: typing.ClassVar[typing.Optional[_App]]
|
|
111
148
|
_name: typing.Optional[str]
|
|
112
149
|
_description: typing.Optional[str]
|
|
113
|
-
|
|
114
|
-
_functions: dict[str, modal._functions._Function]
|
|
115
|
-
_classes: dict[str, modal.cls._Cls]
|
|
116
|
-
_image: typing.Optional[modal.image._Image]
|
|
117
|
-
_secrets: collections.abc.Sequence[modal.secret._Secret]
|
|
118
|
-
_volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume]
|
|
119
|
-
_web_endpoints: list[str]
|
|
120
|
-
_local_entrypoints: dict[str, _LocalEntrypoint]
|
|
150
|
+
_local_state_attr: typing.Optional[_LocalAppState]
|
|
121
151
|
_app_id: typing.Optional[str]
|
|
122
152
|
_running_app: typing.Optional[modal.running_app.RunningApp]
|
|
123
153
|
_client: typing.Optional[modal.client._Client]
|
|
124
|
-
|
|
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
|
+
...
|
|
125
160
|
|
|
126
161
|
def __init__(
|
|
127
162
|
self,
|
|
@@ -151,7 +186,11 @@ class _App:
|
|
|
151
186
|
|
|
152
187
|
@property
|
|
153
188
|
def is_interactive(self) -> bool:
|
|
154
|
-
"""
|
|
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
|
+
"""
|
|
155
194
|
...
|
|
156
195
|
|
|
157
196
|
@property
|
|
@@ -185,12 +224,30 @@ class _App:
|
|
|
185
224
|
"""
|
|
186
225
|
...
|
|
187
226
|
|
|
188
|
-
def set_description(self, description: str):
|
|
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
|
+
|
|
189
235
|
def _validate_blueprint_value(self, key: str, value: typing.Any): ...
|
|
190
236
|
@property
|
|
191
|
-
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
|
+
|
|
192
246
|
@image.setter
|
|
193
|
-
def image(self, value):
|
|
247
|
+
def image(self, value):
|
|
248
|
+
"""mdmd:hidden"""
|
|
249
|
+
...
|
|
250
|
+
|
|
194
251
|
def _uncreate_all_objects(self): ...
|
|
195
252
|
def _set_local_app(
|
|
196
253
|
self, client: modal.client._Client, running_app: modal.running_app.RunningApp
|
|
@@ -301,37 +358,48 @@ class _App:
|
|
|
301
358
|
def _init_container(self, client: modal.client._Client, running_app: modal.running_app.RunningApp): ...
|
|
302
359
|
@property
|
|
303
360
|
def registered_functions(self) -> dict[str, modal._functions._Function]:
|
|
304
|
-
"""
|
|
361
|
+
"""mdmd:hidden
|
|
362
|
+
All modal.Function objects registered on the app.
|
|
305
363
|
|
|
306
364
|
Note: this property is populated only during the build phase, and it is not
|
|
307
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.
|
|
308
368
|
"""
|
|
309
369
|
...
|
|
310
370
|
|
|
311
371
|
@property
|
|
312
372
|
def registered_classes(self) -> dict[str, modal.cls._Cls]:
|
|
313
|
-
"""
|
|
373
|
+
"""mdmd:hidden
|
|
374
|
+
All modal.Cls objects registered on the app.
|
|
314
375
|
|
|
315
376
|
Note: this property is populated only during the build phase, and it is not
|
|
316
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.
|
|
317
380
|
"""
|
|
318
381
|
...
|
|
319
382
|
|
|
320
383
|
@property
|
|
321
384
|
def registered_entrypoints(self) -> dict[str, _LocalEntrypoint]:
|
|
322
|
-
"""
|
|
385
|
+
"""mdmd:hidden
|
|
386
|
+
All local CLI entrypoints registered on the app.
|
|
323
387
|
|
|
324
388
|
Note: this property is populated only during the build phase, and it is not
|
|
325
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.
|
|
326
391
|
"""
|
|
327
392
|
...
|
|
328
393
|
|
|
329
394
|
@property
|
|
330
395
|
def registered_web_endpoints(self) -> list[str]:
|
|
331
|
-
"""
|
|
396
|
+
"""mdmd:hidden
|
|
397
|
+
Names of web endpoint (ie. webhook) functions registered on the app.
|
|
332
398
|
|
|
333
399
|
Note: this property is populated only during the build phase, and it is not
|
|
334
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.
|
|
335
403
|
"""
|
|
336
404
|
...
|
|
337
405
|
|
|
@@ -419,14 +487,14 @@ class _App:
|
|
|
419
487
|
is_generator: typing.Optional[bool] = None,
|
|
420
488
|
cloud: typing.Optional[str] = None,
|
|
421
489
|
region: typing.Union[str, collections.abc.Sequence[str], None] = None,
|
|
490
|
+
nonpreemptible: bool = False,
|
|
422
491
|
enable_memory_snapshot: bool = False,
|
|
423
492
|
block_network: bool = False,
|
|
424
493
|
restrict_modal_access: bool = False,
|
|
425
|
-
|
|
494
|
+
single_use_containers: bool = False,
|
|
426
495
|
i6pn: typing.Optional[bool] = None,
|
|
427
496
|
include_source: typing.Optional[bool] = None,
|
|
428
497
|
experimental_options: typing.Optional[dict[str, typing.Any]] = None,
|
|
429
|
-
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
430
498
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
431
499
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
432
500
|
_experimental_restrict_output: bool = False,
|
|
@@ -434,8 +502,9 @@ class _App:
|
|
|
434
502
|
concurrency_limit: typing.Optional[int] = None,
|
|
435
503
|
container_idle_timeout: typing.Optional[int] = None,
|
|
436
504
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
437
|
-
|
|
505
|
+
max_inputs: typing.Optional[int] = None,
|
|
438
506
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
507
|
+
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
439
508
|
) -> _FunctionDecoratorType:
|
|
440
509
|
"""Decorator to register a new Modal Function with this App."""
|
|
441
510
|
...
|
|
@@ -473,14 +542,14 @@ class _App:
|
|
|
473
542
|
startup_timeout: typing.Optional[int] = None,
|
|
474
543
|
cloud: typing.Optional[str] = None,
|
|
475
544
|
region: typing.Union[str, collections.abc.Sequence[str], None] = None,
|
|
545
|
+
nonpreemptible: bool = False,
|
|
476
546
|
enable_memory_snapshot: bool = False,
|
|
477
547
|
block_network: bool = False,
|
|
478
548
|
restrict_modal_access: bool = False,
|
|
479
|
-
|
|
549
|
+
single_use_containers: bool = False,
|
|
480
550
|
i6pn: typing.Optional[bool] = None,
|
|
481
551
|
include_source: typing.Optional[bool] = None,
|
|
482
552
|
experimental_options: typing.Optional[dict[str, typing.Any]] = None,
|
|
483
|
-
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
484
553
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
485
554
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
486
555
|
_experimental_restrict_output: bool = False,
|
|
@@ -488,8 +557,9 @@ class _App:
|
|
|
488
557
|
concurrency_limit: typing.Optional[int] = None,
|
|
489
558
|
container_idle_timeout: typing.Optional[int] = None,
|
|
490
559
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
560
|
+
max_inputs: typing.Optional[int] = None,
|
|
491
561
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
492
|
-
|
|
562
|
+
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
493
563
|
) -> collections.abc.Callable[[typing.Union[CLS_T, modal._partial_function._PartialFunction]], CLS_T]:
|
|
494
564
|
"""Decorator to register a new Modal [Cls](https://modal.com/docs/reference/modal.Cls) with this App."""
|
|
495
565
|
...
|
|
@@ -523,6 +593,24 @@ class _App:
|
|
|
523
593
|
"""
|
|
524
594
|
...
|
|
525
595
|
|
|
596
|
+
async def set_tags(
|
|
597
|
+
self, tags: collections.abc.Mapping[str, str], *, client: typing.Optional[modal.client._Client] = None
|
|
598
|
+
) -> None:
|
|
599
|
+
"""Attach key-value metadata to the App.
|
|
600
|
+
|
|
601
|
+
Tag metadata can be used to add organization-specific context to the App and can be
|
|
602
|
+
included in billing reports and other informational APIs. Tags can also be set in
|
|
603
|
+
the App constructor.
|
|
604
|
+
|
|
605
|
+
Any tags set on the App before calling this method will be removed if they are not
|
|
606
|
+
included in the argument (i.e., this method does not have `.update()` semantics).
|
|
607
|
+
"""
|
|
608
|
+
...
|
|
609
|
+
|
|
610
|
+
async def get_tags(self, *, client: typing.Optional[modal.client._Client] = None) -> dict[str, str]:
|
|
611
|
+
"""Get the tags that are currently attached to the App."""
|
|
612
|
+
...
|
|
613
|
+
|
|
526
614
|
def _logs(self, client: typing.Optional[modal.client._Client] = None) -> collections.abc.AsyncGenerator[str, None]:
|
|
527
615
|
"""Stream logs from the app.
|
|
528
616
|
|
|
@@ -581,18 +669,11 @@ class App:
|
|
|
581
669
|
_container_app: typing.ClassVar[typing.Optional[App]]
|
|
582
670
|
_name: typing.Optional[str]
|
|
583
671
|
_description: typing.Optional[str]
|
|
584
|
-
|
|
585
|
-
_functions: dict[str, modal.functions.Function]
|
|
586
|
-
_classes: dict[str, modal.cls.Cls]
|
|
587
|
-
_image: typing.Optional[modal.image.Image]
|
|
588
|
-
_secrets: collections.abc.Sequence[modal.secret.Secret]
|
|
589
|
-
_volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume.Volume]
|
|
590
|
-
_web_endpoints: list[str]
|
|
591
|
-
_local_entrypoints: dict[str, LocalEntrypoint]
|
|
672
|
+
_local_state_attr: typing.Optional[_LocalAppState]
|
|
592
673
|
_app_id: typing.Optional[str]
|
|
593
674
|
_running_app: typing.Optional[modal.running_app.RunningApp]
|
|
594
675
|
_client: typing.Optional[modal.client.Client]
|
|
595
|
-
|
|
676
|
+
_root_load_context: modal._load_context.LoadContext
|
|
596
677
|
|
|
597
678
|
def __init__(
|
|
598
679
|
self,
|
|
@@ -615,6 +696,11 @@ class App:
|
|
|
615
696
|
"""
|
|
616
697
|
...
|
|
617
698
|
|
|
699
|
+
@property
|
|
700
|
+
def _local_state(self) -> _LocalAppState:
|
|
701
|
+
"""For internal use only. Do not use this property directly."""
|
|
702
|
+
...
|
|
703
|
+
|
|
618
704
|
@property
|
|
619
705
|
def name(self) -> typing.Optional[str]:
|
|
620
706
|
"""The user-provided name of the App."""
|
|
@@ -622,7 +708,11 @@ class App:
|
|
|
622
708
|
|
|
623
709
|
@property
|
|
624
710
|
def is_interactive(self) -> bool:
|
|
625
|
-
"""
|
|
711
|
+
"""mdmd:hidden
|
|
712
|
+
Whether the current app for the app is running in interactive mode.
|
|
713
|
+
|
|
714
|
+
Note: this method will likely be deprecated in the future.
|
|
715
|
+
"""
|
|
626
716
|
...
|
|
627
717
|
|
|
628
718
|
@property
|
|
@@ -680,17 +770,35 @@ class App:
|
|
|
680
770
|
"""
|
|
681
771
|
...
|
|
682
772
|
|
|
683
|
-
lookup: __lookup_spec
|
|
773
|
+
lookup: typing.ClassVar[__lookup_spec]
|
|
774
|
+
|
|
775
|
+
def set_description(self, description: str):
|
|
776
|
+
"""mdmd:hidden
|
|
777
|
+
Set the description of the App before it starts running.
|
|
778
|
+
|
|
779
|
+
Note: we don't recommend using the method and may deprecate it in the future.
|
|
780
|
+
"""
|
|
781
|
+
...
|
|
684
782
|
|
|
685
|
-
def set_description(self, description: str): ...
|
|
686
783
|
def _validate_blueprint_value(self, key: str, value: typing.Any): ...
|
|
687
784
|
@property
|
|
688
|
-
def image(self) -> modal.image.Image:
|
|
785
|
+
def image(self) -> modal.image.Image:
|
|
786
|
+
"""mdmd:hidden
|
|
787
|
+
Retrieve the Image that will be used as the default for any Functions registered to the App.
|
|
788
|
+
|
|
789
|
+
Note: This property is only relevant in the build phase and won't be populated on a deployed
|
|
790
|
+
App that is retrieved via `modal.App.lookup`. It is likely to be deprecated in the future.
|
|
791
|
+
"""
|
|
792
|
+
...
|
|
793
|
+
|
|
689
794
|
@image.setter
|
|
690
|
-
def image(self, value):
|
|
795
|
+
def image(self, value):
|
|
796
|
+
"""mdmd:hidden"""
|
|
797
|
+
...
|
|
798
|
+
|
|
691
799
|
def _uncreate_all_objects(self): ...
|
|
692
800
|
|
|
693
|
-
class ___set_local_app_spec(typing_extensions.Protocol
|
|
801
|
+
class ___set_local_app_spec(typing_extensions.Protocol):
|
|
694
802
|
def __call__(
|
|
695
803
|
self, /, client: modal.client.Client, running_app: modal.running_app.RunningApp
|
|
696
804
|
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[None]: ...
|
|
@@ -698,9 +806,9 @@ class App:
|
|
|
698
806
|
self, /, client: modal.client.Client, running_app: modal.running_app.RunningApp
|
|
699
807
|
) -> typing.AsyncContextManager[None]: ...
|
|
700
808
|
|
|
701
|
-
_set_local_app: ___set_local_app_spec
|
|
809
|
+
_set_local_app: ___set_local_app_spec
|
|
702
810
|
|
|
703
|
-
class __run_spec(typing_extensions.Protocol
|
|
811
|
+
class __run_spec(typing_extensions.Protocol):
|
|
704
812
|
def __call__(
|
|
705
813
|
self,
|
|
706
814
|
/,
|
|
@@ -799,7 +907,7 @@ class App:
|
|
|
799
907
|
"""
|
|
800
908
|
...
|
|
801
909
|
|
|
802
|
-
run: __run_spec
|
|
910
|
+
run: __run_spec
|
|
803
911
|
|
|
804
912
|
class __deploy_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
805
913
|
def __call__(
|
|
@@ -915,37 +1023,48 @@ class App:
|
|
|
915
1023
|
def _init_container(self, client: modal.client.Client, running_app: modal.running_app.RunningApp): ...
|
|
916
1024
|
@property
|
|
917
1025
|
def registered_functions(self) -> dict[str, modal.functions.Function]:
|
|
918
|
-
"""
|
|
1026
|
+
"""mdmd:hidden
|
|
1027
|
+
All modal.Function objects registered on the app.
|
|
919
1028
|
|
|
920
1029
|
Note: this property is populated only during the build phase, and it is not
|
|
921
1030
|
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
1031
|
+
This method is likely to be deprecated in the future in favor of a different
|
|
1032
|
+
approach for retrieving the layout of a deployed App.
|
|
922
1033
|
"""
|
|
923
1034
|
...
|
|
924
1035
|
|
|
925
1036
|
@property
|
|
926
1037
|
def registered_classes(self) -> dict[str, modal.cls.Cls]:
|
|
927
|
-
"""
|
|
1038
|
+
"""mdmd:hidden
|
|
1039
|
+
All modal.Cls objects registered on the app.
|
|
928
1040
|
|
|
929
1041
|
Note: this property is populated only during the build phase, and it is not
|
|
930
1042
|
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
1043
|
+
This method is likely to be deprecated in the future in favor of a different
|
|
1044
|
+
approach for retrieving the layout of a deployed App.
|
|
931
1045
|
"""
|
|
932
1046
|
...
|
|
933
1047
|
|
|
934
1048
|
@property
|
|
935
1049
|
def registered_entrypoints(self) -> dict[str, LocalEntrypoint]:
|
|
936
|
-
"""
|
|
1050
|
+
"""mdmd:hidden
|
|
1051
|
+
All local CLI entrypoints registered on the app.
|
|
937
1052
|
|
|
938
1053
|
Note: this property is populated only during the build phase, and it is not
|
|
939
1054
|
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
1055
|
+
This method is likely to be deprecated in the future.
|
|
940
1056
|
"""
|
|
941
1057
|
...
|
|
942
1058
|
|
|
943
1059
|
@property
|
|
944
1060
|
def registered_web_endpoints(self) -> list[str]:
|
|
945
|
-
"""
|
|
1061
|
+
"""mdmd:hidden
|
|
1062
|
+
Names of web endpoint (ie. webhook) functions registered on the app.
|
|
946
1063
|
|
|
947
1064
|
Note: this property is populated only during the build phase, and it is not
|
|
948
1065
|
expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
|
|
1066
|
+
This method is likely to be deprecated in the future in favor of a different
|
|
1067
|
+
approach for retrieving the layout of a deployed App.
|
|
949
1068
|
"""
|
|
950
1069
|
...
|
|
951
1070
|
|
|
@@ -1033,14 +1152,14 @@ class App:
|
|
|
1033
1152
|
is_generator: typing.Optional[bool] = None,
|
|
1034
1153
|
cloud: typing.Optional[str] = None,
|
|
1035
1154
|
region: typing.Union[str, collections.abc.Sequence[str], None] = None,
|
|
1155
|
+
nonpreemptible: bool = False,
|
|
1036
1156
|
enable_memory_snapshot: bool = False,
|
|
1037
1157
|
block_network: bool = False,
|
|
1038
1158
|
restrict_modal_access: bool = False,
|
|
1039
|
-
|
|
1159
|
+
single_use_containers: bool = False,
|
|
1040
1160
|
i6pn: typing.Optional[bool] = None,
|
|
1041
1161
|
include_source: typing.Optional[bool] = None,
|
|
1042
1162
|
experimental_options: typing.Optional[dict[str, typing.Any]] = None,
|
|
1043
|
-
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
1044
1163
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
1045
1164
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
1046
1165
|
_experimental_restrict_output: bool = False,
|
|
@@ -1048,8 +1167,9 @@ class App:
|
|
|
1048
1167
|
concurrency_limit: typing.Optional[int] = None,
|
|
1049
1168
|
container_idle_timeout: typing.Optional[int] = None,
|
|
1050
1169
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
1051
|
-
|
|
1170
|
+
max_inputs: typing.Optional[int] = None,
|
|
1052
1171
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
1172
|
+
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
1053
1173
|
) -> _FunctionDecoratorType:
|
|
1054
1174
|
"""Decorator to register a new Modal Function with this App."""
|
|
1055
1175
|
...
|
|
@@ -1087,14 +1207,14 @@ class App:
|
|
|
1087
1207
|
startup_timeout: typing.Optional[int] = None,
|
|
1088
1208
|
cloud: typing.Optional[str] = None,
|
|
1089
1209
|
region: typing.Union[str, collections.abc.Sequence[str], None] = None,
|
|
1210
|
+
nonpreemptible: bool = False,
|
|
1090
1211
|
enable_memory_snapshot: bool = False,
|
|
1091
1212
|
block_network: bool = False,
|
|
1092
1213
|
restrict_modal_access: bool = False,
|
|
1093
|
-
|
|
1214
|
+
single_use_containers: bool = False,
|
|
1094
1215
|
i6pn: typing.Optional[bool] = None,
|
|
1095
1216
|
include_source: typing.Optional[bool] = None,
|
|
1096
1217
|
experimental_options: typing.Optional[dict[str, typing.Any]] = None,
|
|
1097
|
-
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
1098
1218
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
1099
1219
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
1100
1220
|
_experimental_restrict_output: bool = False,
|
|
@@ -1102,8 +1222,9 @@ class App:
|
|
|
1102
1222
|
concurrency_limit: typing.Optional[int] = None,
|
|
1103
1223
|
container_idle_timeout: typing.Optional[int] = None,
|
|
1104
1224
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
1225
|
+
max_inputs: typing.Optional[int] = None,
|
|
1105
1226
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
1106
|
-
|
|
1227
|
+
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
1107
1228
|
) -> collections.abc.Callable[[typing.Union[CLS_T, modal.partial_function.PartialFunction]], CLS_T]:
|
|
1108
1229
|
"""Decorator to register a new Modal [Cls](https://modal.com/docs/reference/modal.Cls) with this App."""
|
|
1109
1230
|
...
|
|
@@ -1137,7 +1258,49 @@ class App:
|
|
|
1137
1258
|
"""
|
|
1138
1259
|
...
|
|
1139
1260
|
|
|
1140
|
-
class
|
|
1261
|
+
class __set_tags_spec(typing_extensions.Protocol):
|
|
1262
|
+
def __call__(
|
|
1263
|
+
self, /, tags: collections.abc.Mapping[str, str], *, client: typing.Optional[modal.client.Client] = None
|
|
1264
|
+
) -> None:
|
|
1265
|
+
"""Attach key-value metadata to the App.
|
|
1266
|
+
|
|
1267
|
+
Tag metadata can be used to add organization-specific context to the App and can be
|
|
1268
|
+
included in billing reports and other informational APIs. Tags can also be set in
|
|
1269
|
+
the App constructor.
|
|
1270
|
+
|
|
1271
|
+
Any tags set on the App before calling this method will be removed if they are not
|
|
1272
|
+
included in the argument (i.e., this method does not have `.update()` semantics).
|
|
1273
|
+
"""
|
|
1274
|
+
...
|
|
1275
|
+
|
|
1276
|
+
async def aio(
|
|
1277
|
+
self, /, tags: collections.abc.Mapping[str, str], *, client: typing.Optional[modal.client.Client] = None
|
|
1278
|
+
) -> None:
|
|
1279
|
+
"""Attach key-value metadata to the App.
|
|
1280
|
+
|
|
1281
|
+
Tag metadata can be used to add organization-specific context to the App and can be
|
|
1282
|
+
included in billing reports and other informational APIs. Tags can also be set in
|
|
1283
|
+
the App constructor.
|
|
1284
|
+
|
|
1285
|
+
Any tags set on the App before calling this method will be removed if they are not
|
|
1286
|
+
included in the argument (i.e., this method does not have `.update()` semantics).
|
|
1287
|
+
"""
|
|
1288
|
+
...
|
|
1289
|
+
|
|
1290
|
+
set_tags: __set_tags_spec
|
|
1291
|
+
|
|
1292
|
+
class __get_tags_spec(typing_extensions.Protocol):
|
|
1293
|
+
def __call__(self, /, *, client: typing.Optional[modal.client.Client] = None) -> dict[str, str]:
|
|
1294
|
+
"""Get the tags that are currently attached to the App."""
|
|
1295
|
+
...
|
|
1296
|
+
|
|
1297
|
+
async def aio(self, /, *, client: typing.Optional[modal.client.Client] = None) -> dict[str, str]:
|
|
1298
|
+
"""Get the tags that are currently attached to the App."""
|
|
1299
|
+
...
|
|
1300
|
+
|
|
1301
|
+
get_tags: __get_tags_spec
|
|
1302
|
+
|
|
1303
|
+
class ___logs_spec(typing_extensions.Protocol):
|
|
1141
1304
|
def __call__(self, /, client: typing.Optional[modal.client.Client] = None) -> typing.Generator[str, None, None]:
|
|
1142
1305
|
"""Stream logs from the app.
|
|
1143
1306
|
|
|
@@ -1154,7 +1317,7 @@ class App:
|
|
|
1154
1317
|
"""
|
|
1155
1318
|
...
|
|
1156
1319
|
|
|
1157
|
-
_logs: ___logs_spec
|
|
1320
|
+
_logs: ___logs_spec
|
|
1158
1321
|
|
|
1159
1322
|
@classmethod
|
|
1160
1323
|
def _get_container_app(cls) -> typing.Optional[App]:
|
modal/billing.py
ADDED
modal/builder/2025.06.txt
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
aiohappyeyeballs==2.6.1
|
|
2
|
-
aiohttp==3.12.7
|
|
3
|
-
|
|
2
|
+
aiohttp==3.12.7 ; python_version < "3.14"
|
|
3
|
+
aiohttp==3.13.2 ; python_version >= "3.14"
|
|
4
|
+
aiosignal==1.3.2 ; python_version < "3.14"
|
|
5
|
+
aiosignal==1.4.0 ; python_version >= "3.14"
|
|
4
6
|
async-timeout==5.0.1 ; python_version < "3.11"
|
|
5
7
|
attrs==25.3.0
|
|
6
8
|
cbor2==5.7.0
|
|
7
9
|
certifi==2025.4.26
|
|
8
10
|
frozenlist==1.6.0
|
|
9
|
-
grpclib==0.4.8
|
|
11
|
+
grpclib==0.4.8 ; python_version < "3.14"
|
|
12
|
+
grpclib==0.4.9 ; python_version >= "3.14"
|
|
10
13
|
h2==4.2.0
|
|
11
14
|
hpack==4.1.0
|
|
12
15
|
hyperframe==6.1.0
|
modal/builder/PREVIEW.txt
CHANGED
modal/builder/base-images.json
CHANGED
modal/cli/_download.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Copyright Modal Labs 2023
|
|
2
2
|
import asyncio
|
|
3
3
|
import functools
|
|
4
|
+
import multiprocessing
|
|
4
5
|
import os
|
|
5
6
|
import shutil
|
|
6
7
|
import sys
|
|
@@ -23,12 +24,22 @@ async def _volume_download(
|
|
|
23
24
|
remote_path: str,
|
|
24
25
|
local_destination: Path,
|
|
25
26
|
overwrite: bool,
|
|
26
|
-
|
|
27
|
+
concurrency: Optional[int] = None,
|
|
28
|
+
progress_cb: Optional[Callable] = None,
|
|
27
29
|
):
|
|
30
|
+
if progress_cb is None:
|
|
31
|
+
|
|
32
|
+
def progress_cb(*_, **__):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
if concurrency is None:
|
|
36
|
+
concurrency = max(128, 2 * multiprocessing.cpu_count())
|
|
37
|
+
|
|
28
38
|
is_pipe = local_destination == PIPE_PATH
|
|
29
39
|
|
|
30
40
|
q: asyncio.Queue[tuple[Optional[Path], Optional[FileEntry]]] = asyncio.Queue()
|
|
31
|
-
num_consumers = 1 if is_pipe else
|
|
41
|
+
num_consumers = 1 if is_pipe else concurrency # concurrency limit for downloading files
|
|
42
|
+
download_semaphore = asyncio.Semaphore(concurrency)
|
|
32
43
|
|
|
33
44
|
async def producer():
|
|
34
45
|
iterator: AsyncIterator[FileEntry]
|
|
@@ -86,7 +97,12 @@ async def _volume_download(
|
|
|
86
97
|
|
|
87
98
|
with output_path.open("wb") as fp:
|
|
88
99
|
if isinstance(volume, _Volume):
|
|
89
|
-
b = await volume.
|
|
100
|
+
b = await volume._read_file_into_fileobj(
|
|
101
|
+
path=entry.path,
|
|
102
|
+
fileobj=fp,
|
|
103
|
+
download_semaphore=download_semaphore,
|
|
104
|
+
progress_cb=file_progress_cb,
|
|
105
|
+
)
|
|
90
106
|
else:
|
|
91
107
|
b = 0
|
|
92
108
|
async for chunk in volume.read_file(entry.path):
|
modal/cli/cluster.py
CHANGED
|
@@ -83,7 +83,9 @@ async def shell(
|
|
|
83
83
|
)
|
|
84
84
|
exec_res: api_pb2.ContainerExecResponse = await client.stub.ContainerExec(req)
|
|
85
85
|
if pty:
|
|
86
|
-
await _ContainerProcess(exec_res.exec_id, client).attach()
|
|
86
|
+
await _ContainerProcess(exec_res.exec_id, task_id, client).attach()
|
|
87
87
|
else:
|
|
88
88
|
# TODO: redirect stderr to its own stream?
|
|
89
|
-
await _ContainerProcess(
|
|
89
|
+
await _ContainerProcess(
|
|
90
|
+
exec_res.exec_id, task_id, client, stdout=StreamType.STDOUT, stderr=StreamType.STDOUT
|
|
91
|
+
).wait()
|
modal/cli/config.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# Copyright Modal Labs 2022
|
|
2
|
+
import json
|
|
3
|
+
|
|
2
4
|
import typer
|
|
3
5
|
|
|
4
6
|
from modal._output import make_console
|
|
@@ -25,7 +27,7 @@ def show(redact: bool = typer.Option(True, help="Redact the `token_secret` value
|
|
|
25
27
|
config_dict["token_secret"] = "***"
|
|
26
28
|
|
|
27
29
|
console = make_console()
|
|
28
|
-
console.
|
|
30
|
+
console.print_json(json.dumps(config_dict))
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
SET_DEFAULT_ENV_HELP = """Set the default Modal environment for the active profile
|