modAL 1.4.3__tar.gz → 1.4.3.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {modal-1.4.3 → modal-1.4.3.dev1}/PKG-INFO +2 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/__init__.py +0 -2
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_container_entrypoint.py +1 -21
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_functions.py +161 -161
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_object.py +13 -13
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_output/rich.py +4 -4
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_output/status.py +1 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_partial_function.py +16 -14
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_resolver.py +5 -3
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_resources.py +1 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_runtime/container_io_manager.py +3 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_runtime/container_io_manager.pyi +9 -3
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_runtime/execution_context.py +3 -6
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_runtime/execution_context.pyi +3 -5
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_runtime/user_code_imports.py +4 -4
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_serialization.py +2 -4
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/async_utils.py +1 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/blob_utils.py +8 -82
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/deprecation.py +8 -5
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/docker_utils.py +1 -10
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/function_utils.py +2 -23
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/grpc_utils.py +6 -10
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/mount_utils.py +1 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/name_utils.py +1 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/sandbox_fs_utils.py +30 -90
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/task_command_router_client.py +35 -99
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/app.py +9 -12
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/app.pyi +10 -17
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/_traceback.py +2 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/app.py +59 -75
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/billing.py +21 -27
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/bootstrap.py +11 -15
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/changelog.py +18 -21
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/cluster.py +19 -47
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/config.py +8 -13
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/container.py +51 -90
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/dashboard.py +2 -6
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/dict.py +25 -60
- modal-1.4.3.dev1/modal/cli/entry_point.py +145 -0
- modal-1.4.3.dev1/modal/cli/environment.py +110 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/import_refs.py +1 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/launch.py +15 -39
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/network_file_system.py +46 -57
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/profile.py +8 -12
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/queues.py +42 -64
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/run.py +37 -81
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/secret.py +18 -40
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/shell.py +100 -137
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/token.py +31 -27
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/utils.py +9 -15
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/volume.py +81 -90
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/client.pyi +10 -2
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cls.py +172 -118
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cls.pyi +77 -44
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/config.py +1 -1
- modal-1.4.3.dev1/modal/container_process.py +470 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/container_process.pyi +120 -11
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/dict.py +4 -4
- modal-1.4.3.dev1/modal/environments.py +164 -0
- modal-1.4.3.dev1/modal/environments.pyi +120 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/exception.py +9 -8
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/experimental/flash.py +6 -6
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/experimental/flash.pyi +2 -2
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/file_io.py +11 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/functions.pyi +15 -89
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/image.py +12 -32
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/image.pyi +0 -54
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/io_streams.py +199 -69
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/io_streams.pyi +64 -125
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/mount.py +28 -38
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/network_file_system.py +2 -2
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/object.pyi +6 -6
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/partial_function.pyi +16 -14
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/proxy.py +1 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/queue.py +4 -4
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/runner.py +2 -5
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/runner.pyi +0 -3
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/sandbox.py +158 -166
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/sandbox.pyi +105 -79
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/sandbox_fs.py +147 -307
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/sandbox_fs.pyi +240 -454
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/secret.py +2 -2
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/serving.py +1 -4
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/serving.pyi +0 -3
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/volume.py +20 -149
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/volume.pyi +18 -109
- {modal-1.4.3 → modal-1.4.3.dev1}/modal.egg-info/PKG-INFO +2 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal.egg-info/SOURCES.txt +0 -3
- {modal-1.4.3 → modal-1.4.3.dev1}/modal.egg-info/requires.txt +1 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_docs/gen_cli_docs.py +1 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/api_grpc.py +0 -16
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/api_pb2.py +777 -876
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/api_pb2.pyi +17 -268
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/api_pb2_grpc.py +0 -34
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/api_pb2_grpc.pyi +0 -12
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/modal_api_grpc.py +0 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/task_command_router_grpc.py +0 -80
- modal-1.4.3.dev1/modal_proto/task_command_router_pb2.py +326 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/task_command_router_pb2.pyi +1 -243
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/task_command_router_pb2_grpc.py +0 -170
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/task_command_router_pb2_grpc.pyi +0 -64
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_version/__init__.py +1 -1
- {modal-1.4.3 → modal-1.4.3.dev1}/pyproject.toml +2 -5
- modal-1.4.3/modal/_environments.py +0 -470
- modal-1.4.3/modal/_function_variants.py +0 -249
- modal-1.4.3/modal/cli/_help.py +0 -295
- modal-1.4.3/modal/cli/entry_point.py +0 -132
- modal-1.4.3/modal/cli/environment.py +0 -180
- modal-1.4.3/modal/container_process.py +0 -224
- modal-1.4.3/modal/environments.py +0 -31
- modal-1.4.3/modal/environments.pyi +0 -343
- modal-1.4.3/modal_proto/task_command_router_pb2.py +0 -442
- {modal-1.4.3 → modal-1.4.3.dev1}/LICENSE +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/README.md +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/__main__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_billing.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_clustered_functions.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_clustered_functions.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_grpc_client.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_ipython.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_load_context.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_location.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_logs.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_output/__init__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_output/manager.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_output/pty.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_runtime/__init__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_runtime/asgi.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_runtime/telemetry.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_server.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_traceback.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_tunnel.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_tunnel.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_type_manager.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/__init__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/app_utils.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/browser_utils.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/git_utils.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/hash_utils.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/http_utils.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/logger.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/package_utils.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/shell_utils.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_utils/time_utils.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_vendor/__init__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_vendor/tblib.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_vendor/version.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/_watcher.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/billing.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/builder/2023.12.txt +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/builder/2024.04.txt +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/builder/2024.10.txt +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/builder/2025.06.txt +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/builder/README.md +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/builder/base-images.json +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/call_graph.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/__init__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/_download.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/logo.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/programs/__init__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/programs/vscode.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cli/selector.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/client.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/dict.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/experimental/__init__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/experimental/ipython.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/file_io.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/file_pattern_matcher.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/functions.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/mount.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/network_file_system.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/object.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/output.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/parallel_map.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/parallel_map.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/partial_function.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/proxy.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/py.typed +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/queue.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/retries.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/running_app.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/schedule.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/scheduler_placement.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/secret.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/server.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/server.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/snapshot.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/snapshot.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/stream_type.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/token_flow.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal/token_flow.pyi +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal.egg-info/top_level.txt +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_docs/__init__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/__init__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_proto/py.typed +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/modal_version/__main__.py +0 -0
- {modal-1.4.3 → modal-1.4.3.dev1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: modal
|
|
3
|
-
Version: 1.4.3
|
|
3
|
+
Version: 1.4.3.dev1
|
|
4
4
|
Summary: Python client library for Modal
|
|
5
5
|
Author-email: Modal Labs <support@modal.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -25,6 +25,7 @@ Requires-Dist: protobuf!=4.24.0,<7.0,>=3.19
|
|
|
25
25
|
Requires-Dist: rich>=12.0.0
|
|
26
26
|
Requires-Dist: synchronicity~=0.12.1
|
|
27
27
|
Requires-Dist: toml
|
|
28
|
+
Requires-Dist: typer>=0.9
|
|
28
29
|
Requires-Dist: types-certifi
|
|
29
30
|
Requires-Dist: types-toml
|
|
30
31
|
Requires-Dist: watchfiles
|
|
@@ -17,7 +17,6 @@ try:
|
|
|
17
17
|
from .cloud_bucket_mount import CloudBucketMount
|
|
18
18
|
from .cls import Cls, parameter
|
|
19
19
|
from .dict import Dict
|
|
20
|
-
from .environments import Environment
|
|
21
20
|
from .exception import Error
|
|
22
21
|
from .file_pattern_matcher import FilePatternMatcher
|
|
23
22
|
from .functions import Function, FunctionCall
|
|
@@ -62,7 +61,6 @@ __all__ = [
|
|
|
62
61
|
"Cls",
|
|
63
62
|
"Cron",
|
|
64
63
|
"Dict",
|
|
65
|
-
"Environment",
|
|
66
64
|
"Error",
|
|
67
65
|
"FilePatternMatcher",
|
|
68
66
|
"Function",
|
|
@@ -94,23 +94,6 @@ class DaemonizedThreadPool:
|
|
|
94
94
|
self.inputs.put((func, args))
|
|
95
95
|
|
|
96
96
|
|
|
97
|
-
def call_server(user_code_event_loop: UserCodeEventLoop):
|
|
98
|
-
async def run():
|
|
99
|
-
exit_event = asyncio.Event()
|
|
100
|
-
|
|
101
|
-
def _on_sigterm():
|
|
102
|
-
logger.info("server_received_sigterm")
|
|
103
|
-
exit_event.set()
|
|
104
|
-
|
|
105
|
-
user_code_event_loop.loop.add_signal_handler(signal.SIGTERM, _on_sigterm)
|
|
106
|
-
try:
|
|
107
|
-
await exit_event.wait()
|
|
108
|
-
finally:
|
|
109
|
-
user_code_event_loop.loop.remove_signal_handler(signal.SIGTERM)
|
|
110
|
-
|
|
111
|
-
user_code_event_loop.run(run())
|
|
112
|
-
|
|
113
|
-
|
|
114
97
|
def call_function(
|
|
115
98
|
user_code_event_loop: UserCodeEventLoop,
|
|
116
99
|
container_io_manager: "modal._runtime.container_io_manager.ContainerIOManager",
|
|
@@ -402,10 +385,7 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
402
385
|
)
|
|
403
386
|
|
|
404
387
|
with service.execution_context(event_loop, container_io_manager) as finalized_functions:
|
|
405
|
-
|
|
406
|
-
call_server(event_loop)
|
|
407
|
-
else:
|
|
408
|
-
call_function(event_loop, container_io_manager, finalized_functions, batch_max_size, batch_wait_ms)
|
|
388
|
+
call_function(event_loop, container_io_manager, finalized_functions, batch_max_size, batch_wait_ms)
|
|
409
389
|
|
|
410
390
|
|
|
411
391
|
if __name__ == "__main__":
|
|
@@ -5,7 +5,7 @@ import inspect
|
|
|
5
5
|
import time
|
|
6
6
|
import typing
|
|
7
7
|
import warnings
|
|
8
|
-
from collections.abc import AsyncGenerator, Collection, Sequence
|
|
8
|
+
from collections.abc import AsyncGenerator, Collection, Sequence, Sized
|
|
9
9
|
from dataclasses import dataclass
|
|
10
10
|
from pathlib import PurePosixPath
|
|
11
11
|
from typing import TYPE_CHECKING, Any, AsyncIterator, Callable, Optional, Union
|
|
@@ -18,7 +18,6 @@ from synchronicity.combined_types import MethodWithAio
|
|
|
18
18
|
from modal_proto import api_pb2
|
|
19
19
|
from modal_proto.modal_api_grpc import ModalClientModal
|
|
20
20
|
|
|
21
|
-
from ._function_variants import _FunctionOptions, _make_function_variant
|
|
22
21
|
from ._load_context import LoadContext
|
|
23
22
|
from ._object import _Object, live_method, live_method_gen
|
|
24
23
|
from ._output.pty import get_pty_info
|
|
@@ -27,8 +26,11 @@ from ._resolver import Resolver
|
|
|
27
26
|
from ._resources import convert_fn_config_to_resources_config
|
|
28
27
|
from ._runtime.execution_context import current_input_id, is_local
|
|
29
28
|
from ._serialization import (
|
|
29
|
+
apply_defaults,
|
|
30
30
|
get_callable_schema,
|
|
31
31
|
serialize,
|
|
32
|
+
serialize_proto_params,
|
|
33
|
+
validate_parameter_values,
|
|
32
34
|
)
|
|
33
35
|
from ._traceback import print_server_warnings
|
|
34
36
|
from ._utils.async_utils import (
|
|
@@ -46,7 +48,6 @@ from ._utils.function_utils import (
|
|
|
46
48
|
OUTPUTS_TIMEOUT,
|
|
47
49
|
FunctionInfo,
|
|
48
50
|
_create_input,
|
|
49
|
-
_parse_retries,
|
|
50
51
|
_process_result,
|
|
51
52
|
_stream_function_call_data,
|
|
52
53
|
get_function_type,
|
|
@@ -89,7 +90,7 @@ from .proxy import _Proxy
|
|
|
89
90
|
from .retries import Retries, RetryManager
|
|
90
91
|
from .schedule import Schedule
|
|
91
92
|
from .secret import _Secret
|
|
92
|
-
from .volume import _Volume
|
|
93
|
+
from .volume import _Volume
|
|
93
94
|
|
|
94
95
|
if TYPE_CHECKING:
|
|
95
96
|
import modal.app
|
|
@@ -548,7 +549,26 @@ class FunctionStats:
|
|
|
548
549
|
backlog: int
|
|
549
550
|
num_total_runners: int
|
|
550
551
|
num_running_inputs: int
|
|
551
|
-
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def _parse_retries(
|
|
555
|
+
retries: Optional[Union[int, Retries]],
|
|
556
|
+
source: str = "",
|
|
557
|
+
) -> Optional[api_pb2.FunctionRetryPolicy]:
|
|
558
|
+
if isinstance(retries, int):
|
|
559
|
+
return Retries(
|
|
560
|
+
max_retries=retries,
|
|
561
|
+
initial_delay=1.0,
|
|
562
|
+
backoff_coefficient=1.0,
|
|
563
|
+
)._to_proto()
|
|
564
|
+
elif isinstance(retries, Retries):
|
|
565
|
+
return retries._to_proto()
|
|
566
|
+
elif retries is None:
|
|
567
|
+
return None
|
|
568
|
+
else:
|
|
569
|
+
extra = f" on {source}" if source else ""
|
|
570
|
+
msg = f"Retries parameter must be an integer or instance of modal.Retries. Found: {type(retries)}{extra}."
|
|
571
|
+
raise InvalidError(msg)
|
|
552
572
|
|
|
553
573
|
|
|
554
574
|
@dataclass
|
|
@@ -634,8 +654,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
634
654
|
None # set for 0.67+ class service functions
|
|
635
655
|
)
|
|
636
656
|
_metadata: Optional[api_pb2.FunctionHandleMetadata] = None
|
|
637
|
-
_options: _FunctionOptions
|
|
638
|
-
_base_function: Optional["_Function"] = None
|
|
639
657
|
|
|
640
658
|
@staticmethod
|
|
641
659
|
def from_local(
|
|
@@ -667,7 +685,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
667
685
|
batch_wait_ms: Optional[int] = None,
|
|
668
686
|
cloud: Optional[str] = None,
|
|
669
687
|
region: Optional[Union[str, Sequence[str]]] = None,
|
|
670
|
-
routing_region: Optional[str] = None,
|
|
671
688
|
nonpreemptible: bool = False,
|
|
672
689
|
is_builder_function: bool = False,
|
|
673
690
|
is_auto_snapshot: bool = False,
|
|
@@ -720,9 +737,9 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
720
737
|
|
|
721
738
|
if retry_policy is not None:
|
|
722
739
|
if webhook_config is not None:
|
|
723
|
-
raise InvalidError("Web
|
|
740
|
+
raise InvalidError("Web endpoints do not support retries.")
|
|
724
741
|
if is_generator:
|
|
725
|
-
raise InvalidError("Generator
|
|
742
|
+
raise InvalidError("Generator functions do not support retries.")
|
|
726
743
|
|
|
727
744
|
if timeout is None: # type: ignore[unreachable] # Help users who aren't using type checkers
|
|
728
745
|
raise InvalidError("The `timeout` parameter cannot be set to None: https://modal.com/docs/guide/timeouts")
|
|
@@ -947,7 +964,13 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
947
964
|
|
|
948
965
|
# Relies on dicts being ordered (true as of Python 3.6).
|
|
949
966
|
volume_mounts = [
|
|
950
|
-
|
|
967
|
+
api_pb2.VolumeMount(
|
|
968
|
+
mount_path=path,
|
|
969
|
+
volume_id=volume.object_id,
|
|
970
|
+
allow_background_commits=True,
|
|
971
|
+
read_only=volume._read_only,
|
|
972
|
+
)
|
|
973
|
+
for path, volume in validated_volumes_no_cloud_buckets
|
|
951
974
|
]
|
|
952
975
|
loaded_mount_ids = {m.object_id for m in all_mounts} | {m.object_id for m in image._mount_layers}
|
|
953
976
|
|
|
@@ -1028,7 +1051,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1028
1051
|
supported_output_formats=supported_output_formats,
|
|
1029
1052
|
http_config=http_config,
|
|
1030
1053
|
is_server=is_server,
|
|
1031
|
-
routing_region=routing_region or "",
|
|
1032
1054
|
)
|
|
1033
1055
|
|
|
1034
1056
|
if isinstance(gpu, list):
|
|
@@ -1068,7 +1090,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1068
1090
|
supported_output_formats=supported_output_formats,
|
|
1069
1091
|
http_config=http_config,
|
|
1070
1092
|
is_server=function_definition.is_server,
|
|
1071
|
-
routing_region=function_definition.routing_region,
|
|
1072
1093
|
)
|
|
1073
1094
|
|
|
1074
1095
|
ranked_functions = []
|
|
@@ -1150,6 +1171,130 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1150
1171
|
|
|
1151
1172
|
return obj
|
|
1152
1173
|
|
|
1174
|
+
def _bind_parameters(
|
|
1175
|
+
self,
|
|
1176
|
+
obj: "modal.cls._Obj",
|
|
1177
|
+
options: Optional["modal.cls._ServiceOptions"],
|
|
1178
|
+
args: Sized,
|
|
1179
|
+
kwargs: dict[str, Any],
|
|
1180
|
+
) -> "_Function":
|
|
1181
|
+
"""mdmd:hidden
|
|
1182
|
+
|
|
1183
|
+
Binds a class-function to a specific instance of (init params, options) or a new workspace
|
|
1184
|
+
"""
|
|
1185
|
+
|
|
1186
|
+
parent = self
|
|
1187
|
+
|
|
1188
|
+
async def _load(
|
|
1189
|
+
param_bound_func: _Function,
|
|
1190
|
+
resolver: Resolver,
|
|
1191
|
+
load_context: LoadContext,
|
|
1192
|
+
existing_object_id: Optional[str],
|
|
1193
|
+
):
|
|
1194
|
+
if not parent.is_hydrated:
|
|
1195
|
+
# While the base Object.hydrate() method appears to be idempotent, it's not always safe
|
|
1196
|
+
await parent.hydrate()
|
|
1197
|
+
|
|
1198
|
+
assert parent._client and parent._client.stub
|
|
1199
|
+
|
|
1200
|
+
if (
|
|
1201
|
+
parent._class_parameter_info
|
|
1202
|
+
and parent._class_parameter_info.format == api_pb2.ClassParameterInfo.PARAM_SERIALIZATION_FORMAT_PROTO
|
|
1203
|
+
):
|
|
1204
|
+
if args:
|
|
1205
|
+
# TODO(elias) - We could potentially support positional args as well, if we want to?
|
|
1206
|
+
raise InvalidError(
|
|
1207
|
+
"Can't use positional arguments with modal.parameter-based synthetic constructors.\n"
|
|
1208
|
+
"Use (<parameter_name>=value) keyword arguments when constructing classes instead."
|
|
1209
|
+
)
|
|
1210
|
+
schema = parent._class_parameter_info.schema
|
|
1211
|
+
kwargs_with_defaults = apply_defaults(kwargs, schema)
|
|
1212
|
+
validate_parameter_values(kwargs_with_defaults, schema)
|
|
1213
|
+
serialized_params = serialize_proto_params(kwargs_with_defaults)
|
|
1214
|
+
can_use_parent = len(parent._class_parameter_info.schema) == 0 # no parameters
|
|
1215
|
+
else:
|
|
1216
|
+
from modal.cls import _ServiceOptions # Should probably define this dataclass here?
|
|
1217
|
+
|
|
1218
|
+
can_use_parent = len(args) + len(kwargs) == 0 and (options == _ServiceOptions())
|
|
1219
|
+
serialized_params = serialize((args, kwargs))
|
|
1220
|
+
|
|
1221
|
+
if can_use_parent:
|
|
1222
|
+
# We can end up here if parent wasn't hydrated when class was instantiated, but has been since.
|
|
1223
|
+
param_bound_func._hydrate_from_other(parent)
|
|
1224
|
+
return
|
|
1225
|
+
|
|
1226
|
+
assert parent is not None and parent.is_hydrated
|
|
1227
|
+
|
|
1228
|
+
if options:
|
|
1229
|
+
# Validate that the same volume (by object_id) isn't mounted at multiple paths
|
|
1230
|
+
validate_volumes_by_object_id(options.validated_volumes)
|
|
1231
|
+
|
|
1232
|
+
volume_mounts = [
|
|
1233
|
+
api_pb2.VolumeMount(
|
|
1234
|
+
mount_path=path,
|
|
1235
|
+
volume_id=volume.object_id,
|
|
1236
|
+
allow_background_commits=True,
|
|
1237
|
+
read_only=volume._read_only,
|
|
1238
|
+
)
|
|
1239
|
+
for path, volume in options.validated_volumes
|
|
1240
|
+
]
|
|
1241
|
+
options_pb = api_pb2.FunctionOptions(
|
|
1242
|
+
secret_ids=[s.object_id for s in options.secrets],
|
|
1243
|
+
replace_secret_ids=bool(options.secrets),
|
|
1244
|
+
replace_volume_mounts=len(volume_mounts) > 0,
|
|
1245
|
+
volume_mounts=volume_mounts,
|
|
1246
|
+
cloud_bucket_mounts=cloud_bucket_mounts_to_proto(options.cloud_bucket_mounts),
|
|
1247
|
+
replace_cloud_bucket_mounts=bool(options.cloud_bucket_mounts),
|
|
1248
|
+
resources=options.resources,
|
|
1249
|
+
retry_policy=options.retry_policy,
|
|
1250
|
+
concurrency_limit=options.max_containers,
|
|
1251
|
+
buffer_containers=options.buffer_containers,
|
|
1252
|
+
task_idle_timeout_secs=options.scaledown_window,
|
|
1253
|
+
timeout_secs=options.timeout_secs,
|
|
1254
|
+
max_concurrent_inputs=options.max_concurrent_inputs,
|
|
1255
|
+
target_concurrent_inputs=options.target_concurrent_inputs,
|
|
1256
|
+
batch_max_size=options.batch_max_size,
|
|
1257
|
+
batch_linger_ms=options.batch_wait_ms,
|
|
1258
|
+
scheduler_placement=options.scheduler_placement,
|
|
1259
|
+
cloud_provider_str=options.cloud,
|
|
1260
|
+
)
|
|
1261
|
+
else:
|
|
1262
|
+
options_pb = None
|
|
1263
|
+
|
|
1264
|
+
req = api_pb2.FunctionBindParamsRequest(
|
|
1265
|
+
function_id=parent.object_id,
|
|
1266
|
+
serialized_params=serialized_params,
|
|
1267
|
+
function_options=options_pb,
|
|
1268
|
+
environment_name=load_context.environment_name
|
|
1269
|
+
or "", # TODO: investigate shouldn't environment name always be specified here?
|
|
1270
|
+
)
|
|
1271
|
+
|
|
1272
|
+
response = await parent._client.stub.FunctionBindParams(req)
|
|
1273
|
+
param_bound_func._hydrate(response.bound_function_id, parent._client, response.handle_metadata)
|
|
1274
|
+
|
|
1275
|
+
def _deps():
|
|
1276
|
+
if options:
|
|
1277
|
+
all_deps = (
|
|
1278
|
+
[v for _, v in options.validated_volumes]
|
|
1279
|
+
+ list(options.secrets)
|
|
1280
|
+
+ [mount.secret for _, mount in options.cloud_bucket_mounts if mount.secret]
|
|
1281
|
+
)
|
|
1282
|
+
return [dep for dep in all_deps if not dep.is_hydrated]
|
|
1283
|
+
return []
|
|
1284
|
+
|
|
1285
|
+
fun: _Function = _Function._from_loader(
|
|
1286
|
+
_load,
|
|
1287
|
+
"Function(parametrized)",
|
|
1288
|
+
hydrate_lazily=True,
|
|
1289
|
+
deps=_deps,
|
|
1290
|
+
load_context_overrides=self._load_context_overrides,
|
|
1291
|
+
)
|
|
1292
|
+
|
|
1293
|
+
fun._info = self._info
|
|
1294
|
+
fun._obj = obj
|
|
1295
|
+
fun._spec = self._spec # TODO (elias): fix - this is incorrect when using with_options
|
|
1296
|
+
return fun
|
|
1297
|
+
|
|
1153
1298
|
@live_method
|
|
1154
1299
|
async def update_autoscaler(
|
|
1155
1300
|
self,
|
|
@@ -1239,7 +1384,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1239
1384
|
)
|
|
1240
1385
|
rep = f"modal.Function.from_name('{app_name}', '{name}'{environment_rep})"
|
|
1241
1386
|
return cls._from_loader(
|
|
1242
|
-
_load_remote, rep,
|
|
1387
|
+
_load_remote, rep, is_another_app=True, hydrate_lazily=True, load_context_overrides=load_context_overrides
|
|
1243
1388
|
)
|
|
1244
1389
|
|
|
1245
1390
|
@classmethod
|
|
@@ -1326,8 +1471,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1326
1471
|
self._serve_mounts = frozenset()
|
|
1327
1472
|
self._metadata = None
|
|
1328
1473
|
self._experimental_flash_urls = None
|
|
1329
|
-
self._options = _FunctionOptions()
|
|
1330
|
-
self._base_function = None
|
|
1331
1474
|
|
|
1332
1475
|
def _hydrate_metadata(self, metadata: Optional[Message]):
|
|
1333
1476
|
# Overridden concrete implementation of base class method
|
|
@@ -1388,10 +1531,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1388
1531
|
|
|
1389
1532
|
@live_method
|
|
1390
1533
|
async def get_web_url(self) -> Optional[str]:
|
|
1391
|
-
"""URL
|
|
1392
|
-
|
|
1393
|
-
Returns None when this is not a Web Function.
|
|
1394
|
-
"""
|
|
1534
|
+
"""URL of a Function running as a web endpoint."""
|
|
1395
1535
|
return self._web_url
|
|
1396
1536
|
|
|
1397
1537
|
@live_method
|
|
@@ -1399,143 +1539,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1399
1539
|
"""URL of the flash service for the function."""
|
|
1400
1540
|
return list(self._experimental_flash_urls) if self._experimental_flash_urls else None
|
|
1401
1541
|
|
|
1402
|
-
def _apply_dynamic_config(
|
|
1403
|
-
self,
|
|
1404
|
-
new_options: _FunctionOptions,
|
|
1405
|
-
config_method_name: str,
|
|
1406
|
-
) -> "_Function[P, ReturnType, OriginalReturnType]":
|
|
1407
|
-
base_function = self._base_function or self
|
|
1408
|
-
combined_options = self._options.merge_options(new_options)
|
|
1409
|
-
|
|
1410
|
-
# For "True Functions" (i.e. not the Function inside a Cls), we will use
|
|
1411
|
-
# `parameter_schema=[]` when the Function is un-parameterized. Note that
|
|
1412
|
-
# `parameter_schema=Null` has different semantics that preserve Cls backwards compatibility.
|
|
1413
|
-
parameter_schema: Sequence[api_pb2.ClassParameterSpec] = []
|
|
1414
|
-
args = ()
|
|
1415
|
-
kwargs: dict[str, Any] = {}
|
|
1416
|
-
|
|
1417
|
-
# The main reason to have two layers here (_apply_dynamic_config -> _make_function_variant)
|
|
1418
|
-
# is because the _make_function_variant logic is shared between a true Function and a
|
|
1419
|
-
# "class service function", but we need to propagate different instance metadata to the
|
|
1420
|
-
# new object in each case. There's likely a cleaner way to do this, and then we could just
|
|
1421
|
-
# collapse the dynamic configuration into a single layer.
|
|
1422
|
-
new_function = _make_function_variant(base_function, combined_options, parameter_schema, args, kwargs)
|
|
1423
|
-
|
|
1424
|
-
new_function._options = combined_options
|
|
1425
|
-
new_function._base_function = base_function
|
|
1426
|
-
new_function._rep = f"{self._rep}.{config_method_name}(...)"
|
|
1427
|
-
|
|
1428
|
-
new_function._app = self._app
|
|
1429
|
-
new_function._obj = self._obj
|
|
1430
|
-
|
|
1431
|
-
new_function._is_generator = self._is_generator
|
|
1432
|
-
new_function._webhook_config = self._webhook_config
|
|
1433
|
-
|
|
1434
|
-
# Other fields not necessarily initialized
|
|
1435
|
-
if self._info is not None:
|
|
1436
|
-
new_function._build_args = self._build_args
|
|
1437
|
-
new_function._is_method = self._is_method
|
|
1438
|
-
new_function._raw_f = self._raw_f
|
|
1439
|
-
new_function._tag = self._tag
|
|
1440
|
-
|
|
1441
|
-
return new_function
|
|
1442
|
-
|
|
1443
|
-
def with_options(
|
|
1444
|
-
self,
|
|
1445
|
-
*,
|
|
1446
|
-
cpu: Optional[Union[float, tuple[float, float]]] = None,
|
|
1447
|
-
memory: Optional[Union[int, tuple[int, int]]] = None,
|
|
1448
|
-
gpu: Optional[str] = None,
|
|
1449
|
-
env: Optional[dict[str, Optional[str]]] = None,
|
|
1450
|
-
secrets: Optional[Collection[_Secret]] = None,
|
|
1451
|
-
volumes: dict[Union[str, PurePosixPath], Union[_Volume, _CloudBucketMount]] = {},
|
|
1452
|
-
retries: Optional[Union[int, Retries]] = None,
|
|
1453
|
-
max_containers: Optional[int] = None,
|
|
1454
|
-
buffer_containers: Optional[int] = None,
|
|
1455
|
-
scaledown_window: Optional[int] = None,
|
|
1456
|
-
timeout: Optional[int] = None,
|
|
1457
|
-
region: Optional[Union[str, Sequence[str]]] = None,
|
|
1458
|
-
cloud: Optional[str] = None,
|
|
1459
|
-
) -> "_Function[P, ReturnType, OriginalReturnType]":
|
|
1460
|
-
"""Dynamically override the static Function configuration with invocation-specific values.
|
|
1461
|
-
|
|
1462
|
-
This method returns a new Function instance with the dynamic configuration. Invocations of
|
|
1463
|
-
the new Function will run in a distinct container pool and autoscale independently from the
|
|
1464
|
-
base Function (and from other dynamic configurations).
|
|
1465
|
-
|
|
1466
|
-
Note that options cannot be "unset" with this method (i.e., if a GPU is configured in the
|
|
1467
|
-
`@app.cls()` decorator, passing `gpu=None` here will not create a CPU-only instance).
|
|
1468
|
-
Additionally, container arguments like `volumes` and `secrets` will _replace_ the base
|
|
1469
|
-
configuration or any previous use of this method rather than extending it.
|
|
1470
|
-
|
|
1471
|
-
**Usage:**
|
|
1472
|
-
|
|
1473
|
-
You can use this method after looking up a deployed Function:
|
|
1474
|
-
|
|
1475
|
-
```python notest
|
|
1476
|
-
fn = modal.Function.from_name("my_app", "fn").with_options(gpu="H100")
|
|
1477
|
-
fn.remote() # will run on a H100 GPU
|
|
1478
|
-
```
|
|
1479
|
-
|
|
1480
|
-
Or by referencing another Function defined in the same App:
|
|
1481
|
-
|
|
1482
|
-
```python notest
|
|
1483
|
-
@app.function()
|
|
1484
|
-
def fn():
|
|
1485
|
-
...
|
|
1486
|
-
|
|
1487
|
-
# From a local entrypoint or another Function
|
|
1488
|
-
fn.with_options(gpu="H100").remote() # Uses an H100 GPU
|
|
1489
|
-
fn.remote() # Uses the static configuration with no GPU
|
|
1490
|
-
```
|
|
1491
|
-
|
|
1492
|
-
"""
|
|
1493
|
-
options = _FunctionOptions.new(
|
|
1494
|
-
cpu=cpu,
|
|
1495
|
-
memory=memory,
|
|
1496
|
-
gpu=gpu,
|
|
1497
|
-
env=env,
|
|
1498
|
-
secrets=secrets,
|
|
1499
|
-
volumes=volumes,
|
|
1500
|
-
retries=retries,
|
|
1501
|
-
max_containers=max_containers,
|
|
1502
|
-
buffer_containers=buffer_containers,
|
|
1503
|
-
scaledown_window=scaledown_window,
|
|
1504
|
-
timeout=timeout,
|
|
1505
|
-
region=region,
|
|
1506
|
-
cloud=cloud,
|
|
1507
|
-
)
|
|
1508
|
-
|
|
1509
|
-
return self._apply_dynamic_config(options, "with_options")
|
|
1510
|
-
|
|
1511
|
-
def with_concurrency(
|
|
1512
|
-
self,
|
|
1513
|
-
*,
|
|
1514
|
-
max_inputs: int,
|
|
1515
|
-
target_inputs: Optional[int] = None,
|
|
1516
|
-
) -> "_Function[P, ReturnType, OriginalReturnType]":
|
|
1517
|
-
"""Override the static Function configuration with invocation-specific input concurrency.
|
|
1518
|
-
|
|
1519
|
-
Returns a new Function instance that is dynamically configured to behave like a Function using
|
|
1520
|
-
the `@modal.concurrent` decorator. This instance will autoscale independently from the base Function.
|
|
1521
|
-
"""
|
|
1522
|
-
options = _FunctionOptions.new(max_concurrent_inputs=max_inputs, target_concurrent_inputs=target_inputs)
|
|
1523
|
-
return self._apply_dynamic_config(options, "with_concurrency")
|
|
1524
|
-
|
|
1525
|
-
def with_batching(
|
|
1526
|
-
self,
|
|
1527
|
-
*,
|
|
1528
|
-
max_batch_size: int,
|
|
1529
|
-
wait_ms: int,
|
|
1530
|
-
) -> "_Function[P, ReturnType, OriginalReturnType]":
|
|
1531
|
-
"""Override the static Function configuration with invocation-specific dynamic batching.
|
|
1532
|
-
|
|
1533
|
-
Returns a new Function instance that is dynamically configured to behave like a Function using
|
|
1534
|
-
the `@modal.batched` decorator. This instance will autoscale independently from the base Function.
|
|
1535
|
-
"""
|
|
1536
|
-
options = _FunctionOptions.new(batch_max_size=max_batch_size, batch_wait_ms=wait_ms)
|
|
1537
|
-
return self._apply_dynamic_config(options, "with_batching")
|
|
1538
|
-
|
|
1539
1542
|
@property
|
|
1540
1543
|
async def is_generator(self) -> bool:
|
|
1541
1544
|
"""mdmd:hidden"""
|
|
@@ -1848,10 +1851,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1848
1851
|
retry=Retry(total_timeout=10.0),
|
|
1849
1852
|
)
|
|
1850
1853
|
return FunctionStats(
|
|
1851
|
-
backlog=resp.backlog,
|
|
1852
|
-
num_total_runners=resp.num_total_tasks,
|
|
1853
|
-
num_running_inputs=resp.num_running_inputs,
|
|
1854
|
-
input_headroom=resp.input_headroom,
|
|
1854
|
+
backlog=resp.backlog, num_total_runners=resp.num_total_tasks, num_running_inputs=resp.num_running_inputs
|
|
1855
1855
|
)
|
|
1856
1856
|
|
|
1857
1857
|
@live_method
|
|
@@ -82,7 +82,7 @@ class _Object:
|
|
|
82
82
|
_load: Optional[Callable[[Self, Resolver, LoadContext, Optional[str]], Awaitable[None]]] = None
|
|
83
83
|
_preload: Optional[Callable[[Self, Resolver, LoadContext, Optional[str]], Awaitable[None]]]
|
|
84
84
|
_rep: str
|
|
85
|
-
|
|
85
|
+
_is_another_app: bool
|
|
86
86
|
_hydrate_lazily: bool
|
|
87
87
|
_deps: Optional[Callable[..., Sequence["_Object"]]]
|
|
88
88
|
_deduplication_key: Optional[Callable[[], Awaitable[Hashable]]] = None
|
|
@@ -113,9 +113,9 @@ class _Object:
|
|
|
113
113
|
self,
|
|
114
114
|
rep: str,
|
|
115
115
|
load: Optional[Callable[[Self, Resolver, LoadContext, Optional[str]], Awaitable[None]]] = None,
|
|
116
|
+
is_another_app: bool = False,
|
|
116
117
|
preload: Optional[Callable[[Self, Resolver, LoadContext, Optional[str]], Awaitable[None]]] = None,
|
|
117
118
|
hydrate_lazily: bool = False,
|
|
118
|
-
skip_reload: bool = False,
|
|
119
119
|
deps: Optional[Callable[..., Sequence["_Object"]]] = None,
|
|
120
120
|
deduplication_key: Optional[Callable[[], Awaitable[Hashable]]] = None,
|
|
121
121
|
name: Optional[str] = None,
|
|
@@ -123,10 +123,10 @@ class _Object:
|
|
|
123
123
|
load_context_overrides: Optional[LoadContext] = None,
|
|
124
124
|
):
|
|
125
125
|
self._local_uuid = str(uuid.uuid4())
|
|
126
|
-
self._rep = rep
|
|
127
126
|
self._load = load
|
|
128
127
|
self._preload = preload
|
|
129
|
-
self.
|
|
128
|
+
self._rep = rep
|
|
129
|
+
self._is_another_app = is_another_app
|
|
130
130
|
self._hydrate_lazily = hydrate_lazily
|
|
131
131
|
self._deps = deps
|
|
132
132
|
self._deduplication_key = deduplication_key
|
|
@@ -218,7 +218,7 @@ class _Object:
|
|
|
218
218
|
cls,
|
|
219
219
|
load: Callable[[Self, Resolver, LoadContext, Optional[str]], Awaitable[None]],
|
|
220
220
|
rep: str,
|
|
221
|
-
|
|
221
|
+
is_another_app: bool = False,
|
|
222
222
|
preload: Optional[Callable[[Self, Resolver, LoadContext, Optional[str]], Awaitable[None]]] = None,
|
|
223
223
|
hydrate_lazily: bool = False,
|
|
224
224
|
deps: Optional[Callable[..., Sequence["_Object"]]] = None,
|
|
@@ -232,12 +232,12 @@ class _Object:
|
|
|
232
232
|
obj._init(
|
|
233
233
|
rep,
|
|
234
234
|
load,
|
|
235
|
-
|
|
236
|
-
preload
|
|
237
|
-
hydrate_lazily
|
|
238
|
-
deps
|
|
239
|
-
deduplication_key
|
|
240
|
-
name
|
|
235
|
+
is_another_app,
|
|
236
|
+
preload,
|
|
237
|
+
hydrate_lazily,
|
|
238
|
+
deps,
|
|
239
|
+
deduplication_key,
|
|
240
|
+
name,
|
|
241
241
|
load_context_overrides=load_context_overrides,
|
|
242
242
|
)
|
|
243
243
|
return obj
|
|
@@ -268,7 +268,7 @@ class _Object:
|
|
|
268
268
|
object_id: str,
|
|
269
269
|
client: _Client,
|
|
270
270
|
handle_metadata: Optional[Message],
|
|
271
|
-
|
|
271
|
+
is_another_app: bool = False,
|
|
272
272
|
rep: Optional[str] = None,
|
|
273
273
|
) -> Self:
|
|
274
274
|
obj_cls: type[Self]
|
|
@@ -287,7 +287,7 @@ class _Object:
|
|
|
287
287
|
# Instantiate provider
|
|
288
288
|
obj = _Object.__new__(obj_cls)
|
|
289
289
|
rep = rep or f"modal.{obj_cls.__name__.strip('_')}.from_id({object_id!r})"
|
|
290
|
-
obj._init(rep,
|
|
290
|
+
obj._init(rep, is_another_app=is_another_app)
|
|
291
291
|
obj._hydrate(object_id, client, handle_metadata)
|
|
292
292
|
|
|
293
293
|
return obj
|
|
@@ -49,7 +49,7 @@ from modal._output.manager import (
|
|
|
49
49
|
)
|
|
50
50
|
from modal._utils.time_utils import timestamp_to_localized_str
|
|
51
51
|
from modal.config import logger
|
|
52
|
-
from modal.exception import DeprecationError, ServerWarning
|
|
52
|
+
from modal.exception import DeprecationError, PendingDeprecationError, ServerWarning
|
|
53
53
|
from modal_proto import api_pb2
|
|
54
54
|
|
|
55
55
|
if platform.system() == "Windows":
|
|
@@ -359,14 +359,14 @@ class RichOutputManager(OutputManager):
|
|
|
359
359
|
) -> None:
|
|
360
360
|
"""Display a warning, using rich formatting for Modal-specific warnings.
|
|
361
361
|
|
|
362
|
-
Modal warnings (DeprecationError, ServerWarning) are shown
|
|
362
|
+
Modal warnings (DeprecationError, PendingDeprecationError, ServerWarning) are shown
|
|
363
363
|
in a yellow-bordered panel with source context. Other warnings fall back to the
|
|
364
364
|
default Python warning display.
|
|
365
365
|
"""
|
|
366
366
|
# For non-Modal warnings, fall back to the default display
|
|
367
367
|
import modal
|
|
368
368
|
|
|
369
|
-
is_modal_warning = issubclass(category, (DeprecationError, ServerWarning))
|
|
369
|
+
is_modal_warning = issubclass(category, (DeprecationError, PendingDeprecationError, ServerWarning))
|
|
370
370
|
filename_in_modal = modal.__path__ and Path(filename).is_relative_to(modal.__path__[0])
|
|
371
371
|
if not is_modal_warning and not filename_in_modal:
|
|
372
372
|
base_showwarning(warning, category, filename, lineno, file=None, line=None)
|
|
@@ -392,7 +392,7 @@ class RichOutputManager(OutputManager):
|
|
|
392
392
|
pass
|
|
393
393
|
|
|
394
394
|
# Build title
|
|
395
|
-
if issubclass(category, DeprecationError):
|
|
395
|
+
if issubclass(category, (DeprecationError, PendingDeprecationError)):
|
|
396
396
|
title = "Modal Deprecation Warning"
|
|
397
397
|
else:
|
|
398
398
|
title = "Modal Warning"
|
|
@@ -90,7 +90,7 @@ class FunctionCreationStatus:
|
|
|
90
90
|
suffix = _get_suffix_from_web_url_info(url_info)
|
|
91
91
|
class_web_endpoint_method_status_row = self._output_mgr.add_status_row()
|
|
92
92
|
class_web_endpoint_method_status_row.finish(
|
|
93
|
-
f"Created
|
|
93
|
+
f"Created web endpoint for {method_definition.function_name} => [magenta underline]"
|
|
94
94
|
f"{method_definition.web_url}[/magenta underline]{suffix}"
|
|
95
95
|
)
|
|
96
96
|
for custom_domain in method_definition.custom_domain_info:
|