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/cls.pyi
CHANGED
|
@@ -6,6 +6,7 @@ import modal._object
|
|
|
6
6
|
import modal._partial_function
|
|
7
7
|
import modal.app
|
|
8
8
|
import modal.client
|
|
9
|
+
import modal.cloud_bucket_mount
|
|
9
10
|
import modal.functions
|
|
10
11
|
import modal.gpu
|
|
11
12
|
import modal.object
|
|
@@ -14,7 +15,7 @@ import modal.retries
|
|
|
14
15
|
import modal.secret
|
|
15
16
|
import modal.volume
|
|
16
17
|
import modal_proto.api_pb2
|
|
17
|
-
import
|
|
18
|
+
import pathlib
|
|
18
19
|
import typing
|
|
19
20
|
import typing_extensions
|
|
20
21
|
|
|
@@ -24,9 +25,9 @@ def _use_annotation_parameters(user_cls: type) -> bool: ...
|
|
|
24
25
|
def _get_class_constructor_signature(user_cls: type) -> inspect.Signature: ...
|
|
25
26
|
|
|
26
27
|
class _ServiceOptions:
|
|
27
|
-
"""_ServiceOptions(secrets: Collection[modal.secret._Secret] = (), validated_volumes: Sequence[tuple[str, modal.volume._Volume]] = (), resources: Optional[modal_proto.api_pb2.Resources] = None, retry_policy: Optional[modal_proto.api_pb2.FunctionRetryPolicy] = None, max_containers: Optional[int] = None, buffer_containers: Optional[int] = None, scaledown_window: Optional[int] = None, timeout_secs: Optional[int] = None, max_concurrent_inputs: Optional[int] = None, target_concurrent_inputs: Optional[int] = None, batch_max_size: Optional[int] = None, batch_wait_ms: Optional[int] = None)"""
|
|
28
|
+
"""_ServiceOptions(secrets: collections.abc.Collection[modal.secret._Secret] = (), validated_volumes: Sequence[tuple[str, modal.volume._Volume]] = (), resources: Optional[modal_proto.api_pb2.Resources] = None, retry_policy: Optional[modal_proto.api_pb2.FunctionRetryPolicy] = None, max_containers: Optional[int] = None, buffer_containers: Optional[int] = None, scaledown_window: Optional[int] = None, timeout_secs: Optional[int] = None, max_concurrent_inputs: Optional[int] = None, target_concurrent_inputs: Optional[int] = None, batch_max_size: Optional[int] = None, batch_wait_ms: Optional[int] = None, scheduler_placement: Optional[modal_proto.api_pb2.SchedulerPlacement] = None, cloud: Optional[str] = None, cloud_bucket_mounts: Sequence[tuple[str, modal.cloud_bucket_mount._CloudBucketMount]] = ())"""
|
|
28
29
|
|
|
29
|
-
secrets:
|
|
30
|
+
secrets: collections.abc.Collection[modal.secret._Secret]
|
|
30
31
|
validated_volumes: typing.Sequence[tuple[str, modal.volume._Volume]]
|
|
31
32
|
resources: typing.Optional[modal_proto.api_pb2.Resources]
|
|
32
33
|
retry_policy: typing.Optional[modal_proto.api_pb2.FunctionRetryPolicy]
|
|
@@ -38,6 +39,9 @@ class _ServiceOptions:
|
|
|
38
39
|
target_concurrent_inputs: typing.Optional[int]
|
|
39
40
|
batch_max_size: typing.Optional[int]
|
|
40
41
|
batch_wait_ms: typing.Optional[int]
|
|
42
|
+
scheduler_placement: typing.Optional[modal_proto.api_pb2.SchedulerPlacement]
|
|
43
|
+
cloud: typing.Optional[str]
|
|
44
|
+
cloud_bucket_mounts: typing.Sequence[tuple[str, modal.cloud_bucket_mount._CloudBucketMount]]
|
|
41
45
|
|
|
42
46
|
def merge_options(self, new_options: _ServiceOptions) -> _ServiceOptions:
|
|
43
47
|
"""Implement protobuf-like MergeFrom semantics for this dataclass.
|
|
@@ -48,7 +52,7 @@ class _ServiceOptions:
|
|
|
48
52
|
|
|
49
53
|
def __init__(
|
|
50
54
|
self,
|
|
51
|
-
secrets:
|
|
55
|
+
secrets: collections.abc.Collection[modal.secret._Secret] = (),
|
|
52
56
|
validated_volumes: typing.Sequence[tuple[str, modal.volume._Volume]] = (),
|
|
53
57
|
resources: typing.Optional[modal_proto.api_pb2.Resources] = None,
|
|
54
58
|
retry_policy: typing.Optional[modal_proto.api_pb2.FunctionRetryPolicy] = None,
|
|
@@ -60,6 +64,9 @@ class _ServiceOptions:
|
|
|
60
64
|
target_concurrent_inputs: typing.Optional[int] = None,
|
|
61
65
|
batch_max_size: typing.Optional[int] = None,
|
|
62
66
|
batch_wait_ms: typing.Optional[int] = None,
|
|
67
|
+
scheduler_placement: typing.Optional[modal_proto.api_pb2.SchedulerPlacement] = None,
|
|
68
|
+
cloud: typing.Optional[str] = None,
|
|
69
|
+
cloud_bucket_mounts: typing.Sequence[tuple[str, modal.cloud_bucket_mount._CloudBucketMount]] = (),
|
|
63
70
|
) -> None:
|
|
64
71
|
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
65
72
|
...
|
|
@@ -350,6 +357,10 @@ class _Cls(modal._object._Object):
|
|
|
350
357
|
def _get_name(self) -> str: ...
|
|
351
358
|
def _get_class_service_function(self) -> modal._functions._Function: ...
|
|
352
359
|
def _get_method_names(self) -> collections.abc.Collection[str]: ...
|
|
360
|
+
async def _experimental_get_flash_urls(self) -> typing.Optional[list[str]]:
|
|
361
|
+
"""URL of the flash service for the class."""
|
|
362
|
+
...
|
|
363
|
+
|
|
353
364
|
def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
|
|
354
365
|
@staticmethod
|
|
355
366
|
def validate_construction_mechanism(user_cls):
|
|
@@ -369,6 +380,7 @@ class _Cls(modal._object._Object):
|
|
|
369
380
|
*,
|
|
370
381
|
namespace: typing.Any = None,
|
|
371
382
|
environment_name: typing.Optional[str] = None,
|
|
383
|
+
client: typing.Optional[modal.client._Client] = None,
|
|
372
384
|
) -> _Cls:
|
|
373
385
|
"""Reference a Cls from a deployed App by its name.
|
|
374
386
|
|
|
@@ -388,13 +400,19 @@ class _Cls(modal._object._Object):
|
|
|
388
400
|
cpu: typing.Union[float, tuple[float, float], None] = None,
|
|
389
401
|
memory: typing.Union[int, tuple[int, int], None] = None,
|
|
390
402
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig] = None,
|
|
391
|
-
|
|
392
|
-
|
|
403
|
+
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
404
|
+
secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
|
|
405
|
+
volumes: dict[
|
|
406
|
+
typing.Union[str, pathlib.PurePosixPath],
|
|
407
|
+
typing.Union[modal.volume._Volume, modal.cloud_bucket_mount._CloudBucketMount],
|
|
408
|
+
] = {},
|
|
393
409
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
|
394
410
|
max_containers: typing.Optional[int] = None,
|
|
395
411
|
buffer_containers: typing.Optional[int] = None,
|
|
396
412
|
scaledown_window: typing.Optional[int] = None,
|
|
397
413
|
timeout: typing.Optional[int] = None,
|
|
414
|
+
region: typing.Union[str, typing.Sequence[str], None] = None,
|
|
415
|
+
cloud: typing.Optional[str] = None,
|
|
398
416
|
concurrency_limit: typing.Optional[int] = None,
|
|
399
417
|
container_idle_timeout: typing.Optional[int] = None,
|
|
400
418
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
@@ -454,30 +472,6 @@ class _Cls(modal._object._Object):
|
|
|
454
472
|
"""
|
|
455
473
|
...
|
|
456
474
|
|
|
457
|
-
@staticmethod
|
|
458
|
-
async def lookup(
|
|
459
|
-
app_name: str,
|
|
460
|
-
name: str,
|
|
461
|
-
namespace=None,
|
|
462
|
-
client: typing.Optional[modal.client._Client] = None,
|
|
463
|
-
environment_name: typing.Optional[str] = None,
|
|
464
|
-
) -> _Cls:
|
|
465
|
-
"""mdmd:hidden
|
|
466
|
-
Lookup a Cls from a deployed App by its name.
|
|
467
|
-
|
|
468
|
-
DEPRECATED: This method is deprecated in favor of `modal.Cls.from_name`.
|
|
469
|
-
|
|
470
|
-
In contrast to `modal.Cls.from_name`, this is an eager method
|
|
471
|
-
that will hydrate the local object with metadata from Modal servers.
|
|
472
|
-
|
|
473
|
-
```python notest
|
|
474
|
-
Model = modal.Cls.from_name("other-app", "Model")
|
|
475
|
-
model = Model()
|
|
476
|
-
model.inference(...)
|
|
477
|
-
```
|
|
478
|
-
"""
|
|
479
|
-
...
|
|
480
|
-
|
|
481
475
|
def __call__(self, *args, **kwargs) -> _Obj:
|
|
482
476
|
"""This acts as the class constructor."""
|
|
483
477
|
...
|
|
@@ -514,6 +508,18 @@ class Cls(modal.object.Object):
|
|
|
514
508
|
def _get_name(self) -> str: ...
|
|
515
509
|
def _get_class_service_function(self) -> modal.functions.Function: ...
|
|
516
510
|
def _get_method_names(self) -> collections.abc.Collection[str]: ...
|
|
511
|
+
|
|
512
|
+
class ___experimental_get_flash_urls_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
513
|
+
def __call__(self, /) -> typing.Optional[list[str]]:
|
|
514
|
+
"""URL of the flash service for the class."""
|
|
515
|
+
...
|
|
516
|
+
|
|
517
|
+
async def aio(self, /) -> typing.Optional[list[str]]:
|
|
518
|
+
"""URL of the flash service for the class."""
|
|
519
|
+
...
|
|
520
|
+
|
|
521
|
+
_experimental_get_flash_urls: ___experimental_get_flash_urls_spec[typing_extensions.Self]
|
|
522
|
+
|
|
517
523
|
def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
|
|
518
524
|
@staticmethod
|
|
519
525
|
def validate_construction_mechanism(user_cls):
|
|
@@ -533,6 +539,7 @@ class Cls(modal.object.Object):
|
|
|
533
539
|
*,
|
|
534
540
|
namespace: typing.Any = None,
|
|
535
541
|
environment_name: typing.Optional[str] = None,
|
|
542
|
+
client: typing.Optional[modal.client.Client] = None,
|
|
536
543
|
) -> Cls:
|
|
537
544
|
"""Reference a Cls from a deployed App by its name.
|
|
538
545
|
|
|
@@ -552,13 +559,19 @@ class Cls(modal.object.Object):
|
|
|
552
559
|
cpu: typing.Union[float, tuple[float, float], None] = None,
|
|
553
560
|
memory: typing.Union[int, tuple[int, int], None] = None,
|
|
554
561
|
gpu: typing.Union[None, str, modal.gpu._GPUConfig] = None,
|
|
555
|
-
|
|
556
|
-
|
|
562
|
+
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
563
|
+
secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
|
|
564
|
+
volumes: dict[
|
|
565
|
+
typing.Union[str, pathlib.PurePosixPath],
|
|
566
|
+
typing.Union[modal.volume.Volume, modal.cloud_bucket_mount.CloudBucketMount],
|
|
567
|
+
] = {},
|
|
557
568
|
retries: typing.Union[int, modal.retries.Retries, None] = None,
|
|
558
569
|
max_containers: typing.Optional[int] = None,
|
|
559
570
|
buffer_containers: typing.Optional[int] = None,
|
|
560
571
|
scaledown_window: typing.Optional[int] = None,
|
|
561
572
|
timeout: typing.Optional[int] = None,
|
|
573
|
+
region: typing.Union[str, typing.Sequence[str], None] = None,
|
|
574
|
+
cloud: typing.Optional[str] = None,
|
|
562
575
|
concurrency_limit: typing.Optional[int] = None,
|
|
563
576
|
container_idle_timeout: typing.Optional[int] = None,
|
|
564
577
|
allow_concurrent_inputs: typing.Optional[int] = None,
|
|
@@ -618,59 +631,6 @@ class Cls(modal.object.Object):
|
|
|
618
631
|
"""
|
|
619
632
|
...
|
|
620
633
|
|
|
621
|
-
class __lookup_spec(typing_extensions.Protocol):
|
|
622
|
-
def __call__(
|
|
623
|
-
self,
|
|
624
|
-
/,
|
|
625
|
-
app_name: str,
|
|
626
|
-
name: str,
|
|
627
|
-
namespace=None,
|
|
628
|
-
client: typing.Optional[modal.client.Client] = None,
|
|
629
|
-
environment_name: typing.Optional[str] = None,
|
|
630
|
-
) -> Cls:
|
|
631
|
-
"""mdmd:hidden
|
|
632
|
-
Lookup a Cls from a deployed App by its name.
|
|
633
|
-
|
|
634
|
-
DEPRECATED: This method is deprecated in favor of `modal.Cls.from_name`.
|
|
635
|
-
|
|
636
|
-
In contrast to `modal.Cls.from_name`, this is an eager method
|
|
637
|
-
that will hydrate the local object with metadata from Modal servers.
|
|
638
|
-
|
|
639
|
-
```python notest
|
|
640
|
-
Model = modal.Cls.from_name("other-app", "Model")
|
|
641
|
-
model = Model()
|
|
642
|
-
model.inference(...)
|
|
643
|
-
```
|
|
644
|
-
"""
|
|
645
|
-
...
|
|
646
|
-
|
|
647
|
-
async def aio(
|
|
648
|
-
self,
|
|
649
|
-
/,
|
|
650
|
-
app_name: str,
|
|
651
|
-
name: str,
|
|
652
|
-
namespace=None,
|
|
653
|
-
client: typing.Optional[modal.client.Client] = None,
|
|
654
|
-
environment_name: typing.Optional[str] = None,
|
|
655
|
-
) -> Cls:
|
|
656
|
-
"""mdmd:hidden
|
|
657
|
-
Lookup a Cls from a deployed App by its name.
|
|
658
|
-
|
|
659
|
-
DEPRECATED: This method is deprecated in favor of `modal.Cls.from_name`.
|
|
660
|
-
|
|
661
|
-
In contrast to `modal.Cls.from_name`, this is an eager method
|
|
662
|
-
that will hydrate the local object with metadata from Modal servers.
|
|
663
|
-
|
|
664
|
-
```python notest
|
|
665
|
-
Model = modal.Cls.from_name("other-app", "Model")
|
|
666
|
-
model = Model()
|
|
667
|
-
model.inference(...)
|
|
668
|
-
```
|
|
669
|
-
"""
|
|
670
|
-
...
|
|
671
|
-
|
|
672
|
-
lookup: __lookup_spec
|
|
673
|
-
|
|
674
634
|
def __call__(self, *args, **kwargs) -> Obj:
|
|
675
635
|
"""This acts as the class constructor."""
|
|
676
636
|
...
|
modal/config.py
CHANGED
|
@@ -66,11 +66,15 @@ Other possible configuration options are:
|
|
|
66
66
|
* `traceback` (in the .toml file) / `MODAL_TRACEBACK` (as an env var).
|
|
67
67
|
Defaults to False. Enables printing full tracebacks on unexpected CLI
|
|
68
68
|
errors, which can be useful for debugging client issues.
|
|
69
|
-
* `log_pattern` (in the .toml file) / MODAL_LOG_PATTERN` (as an env var).
|
|
70
|
-
Defaults to "[modal-client] %(asctime)s %(message)s"
|
|
69
|
+
* `log_pattern` (in the .toml file) / `MODAL_LOG_PATTERN` (as an env var).
|
|
70
|
+
Defaults to `"[modal-client] %(asctime)s %(message)s"`
|
|
71
71
|
The log formatting pattern that will be used by the modal client itself.
|
|
72
72
|
See https://docs.python.org/3/library/logging.html#logrecord-attributes for available
|
|
73
73
|
log attributes.
|
|
74
|
+
* `dev_suffix` (in the .toml file) / `MODAL_DEV_SUFFIX` (as an env var).
|
|
75
|
+
Overrides the default `-dev` suffix added to URLs generated for web endpoints
|
|
76
|
+
when the App is ephemeral (i.e., created via `modal serve`). Must be a short
|
|
77
|
+
alphanumeric string.
|
|
74
78
|
|
|
75
79
|
Meta-configuration
|
|
76
80
|
------------------
|
|
@@ -85,6 +89,7 @@ Some "meta-options" are set using environment variables only:
|
|
|
85
89
|
|
|
86
90
|
import logging
|
|
87
91
|
import os
|
|
92
|
+
import re
|
|
88
93
|
import typing
|
|
89
94
|
import warnings
|
|
90
95
|
from typing import Any, Callable, Optional
|
|
@@ -94,7 +99,7 @@ from google.protobuf.empty_pb2 import Empty
|
|
|
94
99
|
from modal_proto import api_pb2
|
|
95
100
|
|
|
96
101
|
from ._utils.logger import configure_logger
|
|
97
|
-
from .exception import InvalidError
|
|
102
|
+
from .exception import InvalidError, NotFoundError
|
|
98
103
|
|
|
99
104
|
DEFAULT_SERVER_URL = "https://api.modal.com"
|
|
100
105
|
|
|
@@ -142,7 +147,7 @@ async def _lookup_workspace(server_url: str, token_id: str, token_secret: str) -
|
|
|
142
147
|
|
|
143
148
|
credentials = (token_id, token_secret)
|
|
144
149
|
async with _Client(server_url, api_pb2.CLIENT_TYPE_CLIENT, credentials) as client:
|
|
145
|
-
return await client.stub.WorkspaceNameLookup(Empty(), timeout=3)
|
|
150
|
+
return await client.stub.WorkspaceNameLookup(Empty(), retry=None, timeout=3)
|
|
146
151
|
|
|
147
152
|
|
|
148
153
|
def config_profiles():
|
|
@@ -158,15 +163,15 @@ def _config_active_profile() -> str:
|
|
|
158
163
|
return "default"
|
|
159
164
|
|
|
160
165
|
|
|
161
|
-
def config_set_active_profile(
|
|
166
|
+
def config_set_active_profile(profile: str) -> None:
|
|
162
167
|
"""Set the user's active modal profile by writing it to the `.modal.toml` file."""
|
|
163
|
-
if
|
|
164
|
-
raise
|
|
168
|
+
if profile not in _user_config:
|
|
169
|
+
raise NotFoundError(f"No profile named '{profile}' found in {user_config_path}")
|
|
165
170
|
|
|
166
|
-
for
|
|
167
|
-
|
|
171
|
+
for profile_data in _user_config.values():
|
|
172
|
+
profile_data.pop("active", None)
|
|
168
173
|
|
|
169
|
-
_user_config[
|
|
174
|
+
_user_config[profile]["active"] = True # type: ignore
|
|
170
175
|
_write_user_config(_user_config)
|
|
171
176
|
|
|
172
177
|
|
|
@@ -206,6 +211,12 @@ def _check_value(options: list[str]) -> Callable[[str], str]:
|
|
|
206
211
|
return checker
|
|
207
212
|
|
|
208
213
|
|
|
214
|
+
def _enforce_suffix_rules(x: str) -> str:
|
|
215
|
+
if x and not re.match(r"^[a-zA-Z0-9]{1,8}$", x):
|
|
216
|
+
raise ValueError("Suffix must be an alphanumeric string of no more than 8 characters.")
|
|
217
|
+
return x
|
|
218
|
+
|
|
219
|
+
|
|
209
220
|
class _Setting(typing.NamedTuple):
|
|
210
221
|
default: typing.Any = None
|
|
211
222
|
transform: typing.Callable[[str], typing.Any] = lambda x: x # noqa: E731
|
|
@@ -236,9 +247,17 @@ _SETTINGS = {
|
|
|
236
247
|
"traceback": _Setting(False, transform=_to_boolean),
|
|
237
248
|
"image_builder_version": _Setting(),
|
|
238
249
|
"strict_parameters": _Setting(False, transform=_to_boolean), # For internal/experimental use
|
|
250
|
+
# Allow insecure TLS for the task command router when running locally (testing/dev only)
|
|
251
|
+
"task_command_router_insecure": _Setting(False, transform=_to_boolean),
|
|
239
252
|
"snapshot_debug": _Setting(False, transform=_to_boolean),
|
|
240
253
|
"cuda_checkpoint_path": _Setting("/__modal/.bin/cuda-checkpoint"), # Used for snapshotting GPU memory.
|
|
241
254
|
"build_validation": _Setting("error", transform=_check_value(["error", "warn", "ignore"])),
|
|
255
|
+
# Payload format for function inputs/outputs: 'pickle' (default) or 'cbor'
|
|
256
|
+
"payload_format": _Setting(
|
|
257
|
+
"pickle",
|
|
258
|
+
transform=lambda s: _check_value(["pickle", "cbor"])(s.lower()),
|
|
259
|
+
),
|
|
260
|
+
"dev_suffix": _Setting("", transform=_enforce_suffix_rules),
|
|
242
261
|
}
|
|
243
262
|
|
|
244
263
|
|