modal 1.0.6.dev58__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/__main__.py +3 -4
- modal/_billing.py +80 -0
- modal/_clustered_functions.py +7 -3
- modal/_clustered_functions.pyi +4 -2
- modal/_container_entrypoint.py +41 -49
- modal/_functions.py +424 -195
- modal/_grpc_client.py +171 -0
- modal/_load_context.py +105 -0
- modal/_object.py +68 -20
- modal/_output.py +58 -45
- modal/_partial_function.py +36 -11
- modal/_pty.py +7 -3
- modal/_resolver.py +21 -35
- modal/_runtime/asgi.py +4 -3
- modal/_runtime/container_io_manager.py +301 -186
- modal/_runtime/container_io_manager.pyi +70 -61
- modal/_runtime/execution_context.py +18 -2
- modal/_runtime/execution_context.pyi +4 -1
- modal/_runtime/gpu_memory_snapshot.py +170 -63
- modal/_runtime/user_code_imports.py +28 -58
- modal/_serialization.py +57 -1
- modal/_utils/async_utils.py +33 -12
- modal/_utils/auth_token_manager.py +2 -5
- modal/_utils/blob_utils.py +110 -53
- modal/_utils/function_utils.py +49 -42
- modal/_utils/grpc_utils.py +80 -50
- 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 +219 -83
- modal/app.pyi +229 -56
- modal/billing.py +5 -0
- modal/{requirements → builder}/2025.06.txt +1 -0
- modal/{requirements → builder}/PREVIEW.txt +1 -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 +9 -13
- 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 +58 -16
- modal/cli/secret.py +48 -22
- modal/cli/utils.py +3 -4
- modal/cli/volume.py +28 -25
- modal/client.py +13 -116
- modal/client.pyi +9 -91
- modal/cloud_bucket_mount.py +5 -3
- modal/cloud_bucket_mount.pyi +5 -1
- modal/cls.py +130 -102
- modal/cls.pyi +45 -85
- modal/config.py +29 -10
- modal/container_process.py +291 -13
- modal/container_process.pyi +95 -32
- modal/dict.py +282 -63
- modal/dict.pyi +423 -73
- modal/environments.py +15 -27
- modal/environments.pyi +5 -15
- modal/exception.py +8 -0
- modal/experimental/__init__.py +143 -38
- modal/experimental/flash.py +247 -78
- modal/experimental/flash.pyi +137 -9
- modal/file_io.py +14 -28
- modal/file_io.pyi +2 -2
- modal/file_pattern_matcher.py +25 -16
- modal/functions.pyi +134 -61
- modal/image.py +255 -86
- modal/image.pyi +300 -62
- modal/io_streams.py +436 -126
- modal/io_streams.pyi +236 -171
- modal/mount.py +62 -157
- modal/mount.pyi +45 -172
- modal/network_file_system.py +30 -53
- modal/network_file_system.pyi +16 -76
- modal/object.pyi +42 -8
- modal/parallel_map.py +821 -113
- modal/parallel_map.pyi +134 -0
- modal/partial_function.pyi +4 -1
- modal/proxy.py +16 -7
- modal/proxy.pyi +10 -2
- modal/queue.py +263 -61
- modal/queue.pyi +409 -66
- modal/runner.py +112 -92
- modal/runner.pyi +45 -27
- modal/sandbox.py +451 -124
- modal/sandbox.pyi +513 -67
- modal/secret.py +291 -67
- modal/secret.pyi +425 -19
- modal/serving.py +7 -11
- modal/serving.pyi +7 -8
- modal/snapshot.py +11 -8
- modal/token_flow.py +4 -4
- modal/volume.py +344 -98
- modal/volume.pyi +464 -68
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +9 -8
- modal-1.2.3.dev7.dist-info/RECORD +195 -0
- modal_docs/mdmd/mdmd.py +11 -1
- modal_proto/api.proto +399 -67
- modal_proto/api_grpc.py +241 -1
- modal_proto/api_pb2.py +1395 -1000
- modal_proto/api_pb2.pyi +1239 -79
- modal_proto/api_pb2_grpc.py +499 -4
- modal_proto/api_pb2_grpc.pyi +162 -14
- modal_proto/modal_api_grpc.py +175 -160
- 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-1.0.6.dev58.dist-info/RECORD +0 -183
- 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/{requirements → builder}/base-images.json +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
- {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
- {modal-1.0.6.dev58.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
|
|
@@ -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,26 +147,26 @@ 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
|
-
_classes: dict[str, modal.cls._Cls]
|
|
115
|
-
_image: typing.Optional[modal.image._Image]
|
|
116
|
-
_secrets: collections.abc.Sequence[modal.secret._Secret]
|
|
117
|
-
_volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume]
|
|
118
|
-
_web_endpoints: list[str]
|
|
119
|
-
_local_entrypoints: dict[str, _LocalEntrypoint]
|
|
150
|
+
_local_state_attr: typing.Optional[_LocalAppState]
|
|
120
151
|
_app_id: typing.Optional[str]
|
|
121
152
|
_running_app: typing.Optional[modal.running_app.RunningApp]
|
|
122
153
|
_client: typing.Optional[modal.client._Client]
|
|
123
|
-
|
|
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
|
+
...
|
|
124
160
|
|
|
125
161
|
def __init__(
|
|
126
162
|
self,
|
|
127
163
|
name: typing.Optional[str] = None,
|
|
128
164
|
*,
|
|
165
|
+
tags: typing.Optional[dict[str, str]] = None,
|
|
129
166
|
image: typing.Optional[modal.image._Image] = None,
|
|
130
167
|
secrets: collections.abc.Sequence[modal.secret._Secret] = [],
|
|
131
168
|
volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume] = {},
|
|
132
|
-
include_source:
|
|
169
|
+
include_source: bool = True,
|
|
133
170
|
) -> None:
|
|
134
171
|
"""Construct a new app, optionally with default image, mounts, secrets, or volumes.
|
|
135
172
|
|
|
@@ -149,7 +186,11 @@ class _App:
|
|
|
149
186
|
|
|
150
187
|
@property
|
|
151
188
|
def is_interactive(self) -> bool:
|
|
152
|
-
"""
|
|
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
|
+
"""
|
|
153
194
|
...
|
|
154
195
|
|
|
155
196
|
@property
|
|
@@ -183,12 +224,30 @@ class _App:
|
|
|
183
224
|
"""
|
|
184
225
|
...
|
|
185
226
|
|
|
186
|
-
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
|
+
|
|
187
235
|
def _validate_blueprint_value(self, key: str, value: typing.Any): ...
|
|
188
236
|
@property
|
|
189
|
-
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
|
+
|
|
190
246
|
@image.setter
|
|
191
|
-
def image(self, value):
|
|
247
|
+
def image(self, value):
|
|
248
|
+
"""mdmd:hidden"""
|
|
249
|
+
...
|
|
250
|
+
|
|
192
251
|
def _uncreate_all_objects(self): ...
|
|
193
252
|
def _set_local_app(
|
|
194
253
|
self, client: modal.client._Client, running_app: modal.running_app.RunningApp
|
|
@@ -299,37 +358,48 @@ class _App:
|
|
|
299
358
|
def _init_container(self, client: modal.client._Client, running_app: modal.running_app.RunningApp): ...
|
|
300
359
|
@property
|
|
301
360
|
def registered_functions(self) -> dict[str, modal._functions._Function]:
|
|
302
|
-
"""
|
|
361
|
+
"""mdmd:hidden
|
|
362
|
+
All modal.Function objects registered on the app.
|
|
303
363
|
|
|
304
364
|
Note: this property is populated only during the build phase, and it is not
|
|
305
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.
|
|
306
368
|
"""
|
|
307
369
|
...
|
|
308
370
|
|
|
309
371
|
@property
|
|
310
372
|
def registered_classes(self) -> dict[str, modal.cls._Cls]:
|
|
311
|
-
"""
|
|
373
|
+
"""mdmd:hidden
|
|
374
|
+
All modal.Cls objects registered on the app.
|
|
312
375
|
|
|
313
376
|
Note: this property is populated only during the build phase, and it is not
|
|
314
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.
|
|
315
380
|
"""
|
|
316
381
|
...
|
|
317
382
|
|
|
318
383
|
@property
|
|
319
384
|
def registered_entrypoints(self) -> dict[str, _LocalEntrypoint]:
|
|
320
|
-
"""
|
|
385
|
+
"""mdmd:hidden
|
|
386
|
+
All local CLI entrypoints registered on the app.
|
|
321
387
|
|
|
322
388
|
Note: this property is populated only during the build phase, and it is not
|
|
323
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.
|
|
324
391
|
"""
|
|
325
392
|
...
|
|
326
393
|
|
|
327
394
|
@property
|
|
328
395
|
def registered_web_endpoints(self) -> list[str]:
|
|
329
|
-
"""
|
|
396
|
+
"""mdmd:hidden
|
|
397
|
+
Names of web endpoint (ie. webhook) functions registered on the app.
|
|
330
398
|
|
|
331
399
|
Note: this property is populated only during the build phase, and it is not
|
|
332
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.
|
|
333
403
|
"""
|
|
334
404
|
...
|
|
335
405
|
|
|
@@ -387,11 +457,12 @@ class _App:
|
|
|
387
457
|
|
|
388
458
|
def function(
|
|
389
459
|
self,
|
|
390
|
-
_warn_parentheses_missing
|
|
460
|
+
_warn_parentheses_missing=None,
|
|
391
461
|
*,
|
|
392
462
|
image: typing.Optional[modal.image._Image] = None,
|
|
393
463
|
schedule: typing.Optional[modal.schedule.Schedule] = None,
|
|
394
|
-
|
|
464
|
+
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
465
|
+
secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
|
|
395
466
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig, list[typing.Union[None, str, modal.gpu._GPUConfig]]] = None,
|
|
396
467
|
serialized: bool = False,
|
|
397
468
|
network_file_systems: dict[
|
|
@@ -410,7 +481,8 @@ class _App:
|
|
|
410
481
|
scaledown_window: typing.Optional[int] = None,
|
|
411
482
|
proxy: typing.Optional[modal.proxy._Proxy] = None,
|
|
412
483
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
|
413
|
-
timeout:
|
|
484
|
+
timeout: int = 300,
|
|
485
|
+
startup_timeout: typing.Optional[int] = None,
|
|
414
486
|
name: typing.Optional[str] = None,
|
|
415
487
|
is_generator: typing.Optional[bool] = None,
|
|
416
488
|
cloud: typing.Optional[str] = None,
|
|
@@ -425,13 +497,12 @@ class _App:
|
|
|
425
497
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
426
498
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
427
499
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
428
|
-
|
|
500
|
+
_experimental_restrict_output: bool = False,
|
|
429
501
|
keep_warm: typing.Optional[int] = None,
|
|
430
502
|
concurrency_limit: typing.Optional[int] = None,
|
|
431
503
|
container_idle_timeout: typing.Optional[int] = None,
|
|
432
504
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
433
505
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
434
|
-
allow_cross_region_volumes: typing.Optional[bool] = None,
|
|
435
506
|
) -> _FunctionDecoratorType:
|
|
436
507
|
"""Decorator to register a new Modal Function with this App."""
|
|
437
508
|
...
|
|
@@ -442,10 +513,11 @@ class _App:
|
|
|
442
513
|
)
|
|
443
514
|
def cls(
|
|
444
515
|
self,
|
|
445
|
-
_warn_parentheses_missing
|
|
516
|
+
_warn_parentheses_missing=None,
|
|
446
517
|
*,
|
|
447
518
|
image: typing.Optional[modal.image._Image] = None,
|
|
448
|
-
|
|
519
|
+
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
520
|
+
secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
|
|
449
521
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig, list[typing.Union[None, str, modal.gpu._GPUConfig]]] = None,
|
|
450
522
|
serialized: bool = False,
|
|
451
523
|
network_file_systems: dict[
|
|
@@ -464,7 +536,8 @@ class _App:
|
|
|
464
536
|
scaledown_window: typing.Optional[int] = None,
|
|
465
537
|
proxy: typing.Optional[modal.proxy._Proxy] = None,
|
|
466
538
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
|
467
|
-
timeout:
|
|
539
|
+
timeout: int = 300,
|
|
540
|
+
startup_timeout: typing.Optional[int] = None,
|
|
468
541
|
cloud: typing.Optional[str] = None,
|
|
469
542
|
region: typing.Union[str, collections.abc.Sequence[str], None] = None,
|
|
470
543
|
enable_memory_snapshot: bool = False,
|
|
@@ -477,18 +550,17 @@ class _App:
|
|
|
477
550
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
478
551
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
479
552
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
480
|
-
|
|
553
|
+
_experimental_restrict_output: bool = False,
|
|
481
554
|
keep_warm: typing.Optional[int] = None,
|
|
482
555
|
concurrency_limit: typing.Optional[int] = None,
|
|
483
556
|
container_idle_timeout: typing.Optional[int] = None,
|
|
484
557
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
485
558
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
486
|
-
allow_cross_region_volumes: typing.Optional[bool] = None,
|
|
487
559
|
) -> collections.abc.Callable[[typing.Union[CLS_T, modal._partial_function._PartialFunction]], CLS_T]:
|
|
488
560
|
"""Decorator to register a new Modal [Cls](https://modal.com/docs/reference/modal.Cls) with this App."""
|
|
489
561
|
...
|
|
490
562
|
|
|
491
|
-
def include(self, /, other_app: _App) -> typing_extensions.Self:
|
|
563
|
+
def include(self, /, other_app: _App, inherit_tags: bool = True) -> typing_extensions.Self:
|
|
492
564
|
"""Include another App's objects in this one.
|
|
493
565
|
|
|
494
566
|
Useful for splitting up Modal Apps across different self-contained files.
|
|
@@ -511,9 +583,30 @@ class _App:
|
|
|
511
583
|
# use function declared on the included app
|
|
512
584
|
bar.remote()
|
|
513
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).
|
|
514
589
|
"""
|
|
515
590
|
...
|
|
516
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
|
+
|
|
517
610
|
def _logs(self, client: typing.Optional[modal.client._Client] = None) -> collections.abc.AsyncGenerator[str, None]:
|
|
518
611
|
"""Stream logs from the app.
|
|
519
612
|
|
|
@@ -572,26 +665,21 @@ class App:
|
|
|
572
665
|
_container_app: typing.ClassVar[typing.Optional[App]]
|
|
573
666
|
_name: typing.Optional[str]
|
|
574
667
|
_description: typing.Optional[str]
|
|
575
|
-
|
|
576
|
-
_classes: dict[str, modal.cls.Cls]
|
|
577
|
-
_image: typing.Optional[modal.image.Image]
|
|
578
|
-
_secrets: collections.abc.Sequence[modal.secret.Secret]
|
|
579
|
-
_volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume.Volume]
|
|
580
|
-
_web_endpoints: list[str]
|
|
581
|
-
_local_entrypoints: dict[str, LocalEntrypoint]
|
|
668
|
+
_local_state_attr: typing.Optional[_LocalAppState]
|
|
582
669
|
_app_id: typing.Optional[str]
|
|
583
670
|
_running_app: typing.Optional[modal.running_app.RunningApp]
|
|
584
671
|
_client: typing.Optional[modal.client.Client]
|
|
585
|
-
|
|
672
|
+
_root_load_context: modal._load_context.LoadContext
|
|
586
673
|
|
|
587
674
|
def __init__(
|
|
588
675
|
self,
|
|
589
676
|
name: typing.Optional[str] = None,
|
|
590
677
|
*,
|
|
678
|
+
tags: typing.Optional[dict[str, str]] = None,
|
|
591
679
|
image: typing.Optional[modal.image.Image] = None,
|
|
592
680
|
secrets: collections.abc.Sequence[modal.secret.Secret] = [],
|
|
593
681
|
volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume.Volume] = {},
|
|
594
|
-
include_source:
|
|
682
|
+
include_source: bool = True,
|
|
595
683
|
) -> None:
|
|
596
684
|
"""Construct a new app, optionally with default image, mounts, secrets, or volumes.
|
|
597
685
|
|
|
@@ -604,6 +692,11 @@ class App:
|
|
|
604
692
|
"""
|
|
605
693
|
...
|
|
606
694
|
|
|
695
|
+
@property
|
|
696
|
+
def _local_state(self) -> _LocalAppState:
|
|
697
|
+
"""For internal use only. Do not use this property directly."""
|
|
698
|
+
...
|
|
699
|
+
|
|
607
700
|
@property
|
|
608
701
|
def name(self) -> typing.Optional[str]:
|
|
609
702
|
"""The user-provided name of the App."""
|
|
@@ -611,7 +704,11 @@ class App:
|
|
|
611
704
|
|
|
612
705
|
@property
|
|
613
706
|
def is_interactive(self) -> bool:
|
|
614
|
-
"""
|
|
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
|
+
"""
|
|
615
712
|
...
|
|
616
713
|
|
|
617
714
|
@property
|
|
@@ -671,12 +768,30 @@ class App:
|
|
|
671
768
|
|
|
672
769
|
lookup: __lookup_spec
|
|
673
770
|
|
|
674
|
-
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
|
+
|
|
675
779
|
def _validate_blueprint_value(self, key: str, value: typing.Any): ...
|
|
676
780
|
@property
|
|
677
|
-
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
|
+
|
|
678
790
|
@image.setter
|
|
679
|
-
def image(self, value):
|
|
791
|
+
def image(self, value):
|
|
792
|
+
"""mdmd:hidden"""
|
|
793
|
+
...
|
|
794
|
+
|
|
680
795
|
def _uncreate_all_objects(self): ...
|
|
681
796
|
|
|
682
797
|
class ___set_local_app_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
@@ -904,37 +1019,48 @@ class App:
|
|
|
904
1019
|
def _init_container(self, client: modal.client.Client, running_app: modal.running_app.RunningApp): ...
|
|
905
1020
|
@property
|
|
906
1021
|
def registered_functions(self) -> dict[str, modal.functions.Function]:
|
|
907
|
-
"""
|
|
1022
|
+
"""mdmd:hidden
|
|
1023
|
+
All modal.Function objects registered on the app.
|
|
908
1024
|
|
|
909
1025
|
Note: this property is populated only during the build phase, and it is not
|
|
910
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.
|
|
911
1029
|
"""
|
|
912
1030
|
...
|
|
913
1031
|
|
|
914
1032
|
@property
|
|
915
1033
|
def registered_classes(self) -> dict[str, modal.cls.Cls]:
|
|
916
|
-
"""
|
|
1034
|
+
"""mdmd:hidden
|
|
1035
|
+
All modal.Cls objects registered on the app.
|
|
917
1036
|
|
|
918
1037
|
Note: this property is populated only during the build phase, and it is not
|
|
919
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.
|
|
920
1041
|
"""
|
|
921
1042
|
...
|
|
922
1043
|
|
|
923
1044
|
@property
|
|
924
1045
|
def registered_entrypoints(self) -> dict[str, LocalEntrypoint]:
|
|
925
|
-
"""
|
|
1046
|
+
"""mdmd:hidden
|
|
1047
|
+
All local CLI entrypoints registered on the app.
|
|
926
1048
|
|
|
927
1049
|
Note: this property is populated only during the build phase, and it is not
|
|
928
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.
|
|
929
1052
|
"""
|
|
930
1053
|
...
|
|
931
1054
|
|
|
932
1055
|
@property
|
|
933
1056
|
def registered_web_endpoints(self) -> list[str]:
|
|
934
|
-
"""
|
|
1057
|
+
"""mdmd:hidden
|
|
1058
|
+
Names of web endpoint (ie. webhook) functions registered on the app.
|
|
935
1059
|
|
|
936
1060
|
Note: this property is populated only during the build phase, and it is not
|
|
937
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.
|
|
938
1064
|
"""
|
|
939
1065
|
...
|
|
940
1066
|
|
|
@@ -992,11 +1118,12 @@ class App:
|
|
|
992
1118
|
|
|
993
1119
|
def function(
|
|
994
1120
|
self,
|
|
995
|
-
_warn_parentheses_missing
|
|
1121
|
+
_warn_parentheses_missing=None,
|
|
996
1122
|
*,
|
|
997
1123
|
image: typing.Optional[modal.image.Image] = None,
|
|
998
1124
|
schedule: typing.Optional[modal.schedule.Schedule] = None,
|
|
999
|
-
|
|
1125
|
+
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
1126
|
+
secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
|
|
1000
1127
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig, list[typing.Union[None, str, modal.gpu._GPUConfig]]] = None,
|
|
1001
1128
|
serialized: bool = False,
|
|
1002
1129
|
network_file_systems: dict[
|
|
@@ -1015,7 +1142,8 @@ class App:
|
|
|
1015
1142
|
scaledown_window: typing.Optional[int] = None,
|
|
1016
1143
|
proxy: typing.Optional[modal.proxy.Proxy] = None,
|
|
1017
1144
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
|
1018
|
-
timeout:
|
|
1145
|
+
timeout: int = 300,
|
|
1146
|
+
startup_timeout: typing.Optional[int] = None,
|
|
1019
1147
|
name: typing.Optional[str] = None,
|
|
1020
1148
|
is_generator: typing.Optional[bool] = None,
|
|
1021
1149
|
cloud: typing.Optional[str] = None,
|
|
@@ -1030,13 +1158,12 @@ class App:
|
|
|
1030
1158
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
1031
1159
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
1032
1160
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
1033
|
-
|
|
1161
|
+
_experimental_restrict_output: bool = False,
|
|
1034
1162
|
keep_warm: typing.Optional[int] = None,
|
|
1035
1163
|
concurrency_limit: typing.Optional[int] = None,
|
|
1036
1164
|
container_idle_timeout: typing.Optional[int] = None,
|
|
1037
1165
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
1038
1166
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
1039
|
-
allow_cross_region_volumes: typing.Optional[bool] = None,
|
|
1040
1167
|
) -> _FunctionDecoratorType:
|
|
1041
1168
|
"""Decorator to register a new Modal Function with this App."""
|
|
1042
1169
|
...
|
|
@@ -1047,10 +1174,11 @@ class App:
|
|
|
1047
1174
|
)
|
|
1048
1175
|
def cls(
|
|
1049
1176
|
self,
|
|
1050
|
-
_warn_parentheses_missing
|
|
1177
|
+
_warn_parentheses_missing=None,
|
|
1051
1178
|
*,
|
|
1052
1179
|
image: typing.Optional[modal.image.Image] = None,
|
|
1053
|
-
|
|
1180
|
+
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
1181
|
+
secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
|
|
1054
1182
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig, list[typing.Union[None, str, modal.gpu._GPUConfig]]] = None,
|
|
1055
1183
|
serialized: bool = False,
|
|
1056
1184
|
network_file_systems: dict[
|
|
@@ -1069,7 +1197,8 @@ class App:
|
|
|
1069
1197
|
scaledown_window: typing.Optional[int] = None,
|
|
1070
1198
|
proxy: typing.Optional[modal.proxy.Proxy] = None,
|
|
1071
1199
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
|
1072
|
-
timeout:
|
|
1200
|
+
timeout: int = 300,
|
|
1201
|
+
startup_timeout: typing.Optional[int] = None,
|
|
1073
1202
|
cloud: typing.Optional[str] = None,
|
|
1074
1203
|
region: typing.Union[str, collections.abc.Sequence[str], None] = None,
|
|
1075
1204
|
enable_memory_snapshot: bool = False,
|
|
@@ -1082,18 +1211,17 @@ class App:
|
|
|
1082
1211
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
|
1083
1212
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
|
1084
1213
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
|
1085
|
-
|
|
1214
|
+
_experimental_restrict_output: bool = False,
|
|
1086
1215
|
keep_warm: typing.Optional[int] = None,
|
|
1087
1216
|
concurrency_limit: typing.Optional[int] = None,
|
|
1088
1217
|
container_idle_timeout: typing.Optional[int] = None,
|
|
1089
1218
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
1090
1219
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
|
1091
|
-
allow_cross_region_volumes: typing.Optional[bool] = None,
|
|
1092
1220
|
) -> collections.abc.Callable[[typing.Union[CLS_T, modal.partial_function.PartialFunction]], CLS_T]:
|
|
1093
1221
|
"""Decorator to register a new Modal [Cls](https://modal.com/docs/reference/modal.Cls) with this App."""
|
|
1094
1222
|
...
|
|
1095
1223
|
|
|
1096
|
-
def include(self, /, other_app: App) -> typing_extensions.Self:
|
|
1224
|
+
def include(self, /, other_app: App, inherit_tags: bool = True) -> typing_extensions.Self:
|
|
1097
1225
|
"""Include another App's objects in this one.
|
|
1098
1226
|
|
|
1099
1227
|
Useful for splitting up Modal Apps across different self-contained files.
|
|
@@ -1116,9 +1244,54 @@ class App:
|
|
|
1116
1244
|
# use function declared on the included app
|
|
1117
1245
|
bar.remote()
|
|
1118
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).
|
|
1119
1250
|
"""
|
|
1120
1251
|
...
|
|
1121
1252
|
|
|
1253
|
+
class __set_tags_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
1254
|
+
def __call__(
|
|
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
|
+
|
|
1122
1295
|
class ___logs_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
1123
1296
|
def __call__(self, /, client: typing.Optional[modal.client.Client] = None) -> typing.Generator[str, None, None]:
|
|
1124
1297
|
"""Stream logs from the app.
|
modal/billing.py
ADDED
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/_traceback.py
CHANGED
|
@@ -6,12 +6,13 @@ import re
|
|
|
6
6
|
import warnings
|
|
7
7
|
from typing import Optional
|
|
8
8
|
|
|
9
|
-
from rich.console import
|
|
9
|
+
from rich.console import RenderResult, group
|
|
10
10
|
from rich.panel import Panel
|
|
11
11
|
from rich.syntax import Syntax
|
|
12
12
|
from rich.text import Text
|
|
13
13
|
from rich.traceback import PathHighlighter, Stack, Traceback, install
|
|
14
14
|
|
|
15
|
+
from .._output import make_console
|
|
15
16
|
from ..exception import DeprecationError, PendingDeprecationError, ServerWarning
|
|
16
17
|
|
|
17
18
|
|
|
@@ -193,7 +194,7 @@ def highlight_modal_warnings() -> None:
|
|
|
193
194
|
title=title,
|
|
194
195
|
title_align="left",
|
|
195
196
|
)
|
|
196
|
-
|
|
197
|
+
make_console().print(panel)
|
|
197
198
|
else:
|
|
198
199
|
base_showwarning(warning, category, filename, lineno, file=None, line=None)
|
|
199
200
|
|