modal 1.2.5.dev13__tar.gz → 1.2.5.dev15__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.2.5.dev13 → modal-1.2.5.dev15}/PKG-INFO +1 -1
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_partial_function.py +1 -2
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/user_code_imports.py +5 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/app.py +7 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/client.pyi +2 -2
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/experimental/flash.py +43 -4
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/experimental/flash.pyi +20 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api.proto +5 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api_pb2.py +338 -338
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api_pb2.pyi +8 -1
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_version/__init__.py +1 -1
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/LICENSE +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/README.md +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/__init__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/__main__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_billing.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_clustered_functions.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_clustered_functions.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_container_entrypoint.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_functions.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_grpc_client.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_ipython.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_load_context.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_location.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_object.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_output.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_pty.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_resolver.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_resources.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/__init__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/asgi.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/execution_context.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/telemetry.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_serialization.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_traceback.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_tunnel.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_tunnel.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_type_manager.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/__init__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/app_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/async_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/blob_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/deprecation.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/docker_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/function_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/git_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/hash_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/http_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/logger.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/mount_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/name_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/package_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/shell_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/time_utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_vendor/__init__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_vendor/tblib.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_watcher.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/app.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/billing.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/2023.12.txt +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/2024.04.txt +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/2024.10.txt +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/2025.06.txt +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/README.md +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/base-images.json +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/call_graph.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/__init__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/_download.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/_traceback.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/app.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/cluster.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/config.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/container.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/dict.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/entry_point.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/environment.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/import_refs.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/launch.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/network_file_system.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/profile.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/programs/__init__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/programs/launch_instance_ssh.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/programs/run_marimo.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/programs/vscode.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/queues.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/run.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/secret.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/shell.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/token.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/utils.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/volume.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/client.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cls.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cls.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/config.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/container_process.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/container_process.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/dict.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/dict.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/environments.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/environments.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/exception.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/experimental/__init__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/experimental/ipython.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/file_io.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/file_io.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/file_pattern_matcher.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/functions.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/functions.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/gpu.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/image.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/image.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/io_streams.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/io_streams.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/mount.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/mount.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/network_file_system.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/network_file_system.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/object.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/object.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/output.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/parallel_map.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/parallel_map.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/partial_function.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/partial_function.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/proxy.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/proxy.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/py.typed +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/queue.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/queue.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/retries.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/runner.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/runner.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/running_app.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/sandbox.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/sandbox.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/schedule.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/scheduler_placement.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/secret.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/secret.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/serving.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/serving.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/snapshot.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/snapshot.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/stream_type.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/token_flow.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/token_flow.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/volume.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/volume.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/requires.txt +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/top_level.txt +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/__init__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/__init__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api_grpc.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/py.typed +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router.proto +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router_grpc.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2_grpc.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2_grpc.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router.proto +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_version/__main__.py +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/pyproject.toml +0 -0
- {modal-1.2.5.dev13 → modal-1.2.5.dev15}/setup.cfg +0 -0
|
@@ -160,9 +160,8 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
|
160
160
|
raise InvalidError("Interface decorators cannot be combined with lifecycle decorators.")
|
|
161
161
|
|
|
162
162
|
has_web_interface = self.flags & _PartialFunctionFlags.WEB_INTERFACE
|
|
163
|
-
has_http_web_interface = self.flags & _PartialFunctionFlags.HTTP_WEB_INTERFACE
|
|
164
163
|
has_callable_interface = self.flags & _PartialFunctionFlags.CALLABLE_INTERFACE
|
|
165
|
-
if
|
|
164
|
+
if has_web_interface and has_callable_interface:
|
|
166
165
|
self.registered = True # Hacky, avoid false-positive warning
|
|
167
166
|
raise InvalidError("Callable decorators cannot be combined with web interface decorators.")
|
|
168
167
|
|
|
@@ -30,6 +30,7 @@ from modal._utils.function_utils import (
|
|
|
30
30
|
from modal.app import _App
|
|
31
31
|
from modal.config import logger
|
|
32
32
|
from modal.exception import ExecutionError, InvalidError
|
|
33
|
+
from modal.experimental.flash import _FlashContainerEntry
|
|
33
34
|
from modal_proto import api_pb2
|
|
34
35
|
|
|
35
36
|
if typing.TYPE_CHECKING:
|
|
@@ -392,18 +393,22 @@ class ImportedClass(Service):
|
|
|
392
393
|
event_loop: UserCodeEventLoop,
|
|
393
394
|
container_io_manager: "modal._runtime.container_io_manager.ContainerIOManager",
|
|
394
395
|
):
|
|
396
|
+
flash_entry = _FlashContainerEntry(self.function_def.http_config)
|
|
395
397
|
# Identify the "enter" methods to run after resuming from a snapshot.
|
|
396
398
|
if not self.function_def.is_auto_snapshot:
|
|
397
399
|
post_snapshot_methods = _find_callables_for_obj(
|
|
398
400
|
self.user_cls_instance, _PartialFunctionFlags.ENTER_POST_SNAPSHOT
|
|
399
401
|
)
|
|
400
402
|
call_lifecycle_functions(event_loop, container_io_manager, list(post_snapshot_methods.values()))
|
|
403
|
+
flash_entry.enter()
|
|
401
404
|
try:
|
|
402
405
|
yield
|
|
403
406
|
finally:
|
|
404
407
|
if not self.function_def.is_auto_snapshot:
|
|
408
|
+
flash_entry.stop()
|
|
405
409
|
exit_methods = _find_callables_for_obj(self.user_cls_instance, _PartialFunctionFlags.EXIT)
|
|
406
410
|
call_lifecycle_functions(event_loop, container_io_manager, list(exit_methods.values()))
|
|
411
|
+
flash_entry.close()
|
|
407
412
|
|
|
408
413
|
|
|
409
414
|
def get_user_class_instance(_cls: modal.cls._Cls, args: tuple[Any, ...], kwargs: dict[str, Any]) -> typing.Any:
|
|
@@ -1098,6 +1098,13 @@ class _App:
|
|
|
1098
1098
|
"The `@modal.http_server` decorator cannot be used on methods; decorate the class instead."
|
|
1099
1099
|
)
|
|
1100
1100
|
|
|
1101
|
+
if http_config is not None:
|
|
1102
|
+
for method in _find_partial_methods_for_user_cls(
|
|
1103
|
+
user_cls, _PartialFunctionFlags.CALLABLE_INTERFACE
|
|
1104
|
+
).values():
|
|
1105
|
+
method.registered = True # Avoid warning about not registering the method (hacky!)
|
|
1106
|
+
raise InvalidError("Callable decorators cannot be combined with web interface decorators.")
|
|
1107
|
+
|
|
1101
1108
|
info = FunctionInfo(None, serialized=serialized, user_cls=user_cls)
|
|
1102
1109
|
|
|
1103
1110
|
i6pn_enabled = i6pn or cluster_size is not None
|
|
@@ -32,7 +32,7 @@ class _Client:
|
|
|
32
32
|
server_url: str,
|
|
33
33
|
client_type: int,
|
|
34
34
|
credentials: typing.Optional[tuple[str, str]],
|
|
35
|
-
version: str = "1.2.5.
|
|
35
|
+
version: str = "1.2.5.dev15",
|
|
36
36
|
):
|
|
37
37
|
"""mdmd:hidden
|
|
38
38
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -163,7 +163,7 @@ class Client:
|
|
|
163
163
|
server_url: str,
|
|
164
164
|
client_type: int,
|
|
165
165
|
credentials: typing.Optional[tuple[str, str]],
|
|
166
|
-
version: str = "1.2.5.
|
|
166
|
+
version: str = "1.2.5.dev15",
|
|
167
167
|
):
|
|
168
168
|
"""mdmd:hidden
|
|
169
169
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -32,6 +32,7 @@ class _FlashManager:
|
|
|
32
32
|
process: Optional[subprocess.Popen] = None, # to be deprecated
|
|
33
33
|
health_check_url: Optional[str] = None,
|
|
34
34
|
startup_timeout: int = 30,
|
|
35
|
+
exit_grace_period: int = 0,
|
|
35
36
|
h2_enabled: bool = False,
|
|
36
37
|
):
|
|
37
38
|
self.client = client
|
|
@@ -40,6 +41,7 @@ class _FlashManager:
|
|
|
40
41
|
# Health check is not currently being used
|
|
41
42
|
self.health_check_url = health_check_url
|
|
42
43
|
self.startup_timeout = startup_timeout
|
|
44
|
+
self.exit_grace_period = exit_grace_period
|
|
43
45
|
self.tunnel_manager = _forward_tunnel(port, h2_enabled=h2_enabled, client=client)
|
|
44
46
|
self.stopped = False
|
|
45
47
|
self.num_failures = 0
|
|
@@ -158,9 +160,12 @@ class _FlashManager:
|
|
|
158
160
|
return self.tunnel.url
|
|
159
161
|
|
|
160
162
|
async def stop(self):
|
|
161
|
-
|
|
162
|
-
|
|
163
|
+
try:
|
|
164
|
+
self.heartbeat_task.cancel()
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.error(f"[Modal Flash] Error stopping: {e}")
|
|
163
167
|
|
|
168
|
+
await self.client.stub.FlashContainerDeregister(api_pb2.FlashContainerDeregisterRequest())
|
|
164
169
|
self.stopped = True
|
|
165
170
|
logger.warning(f"[Modal Flash] No longer accepting new requests on {self.tunnel.url}.")
|
|
166
171
|
|
|
@@ -171,19 +176,22 @@ class _FlashManager:
|
|
|
171
176
|
if not self.stopped:
|
|
172
177
|
await self.stop()
|
|
173
178
|
|
|
179
|
+
await asyncio.sleep(self.exit_grace_period)
|
|
180
|
+
|
|
174
181
|
logger.warning(f"[Modal Flash] Closing tunnel on {self.tunnel.url}.")
|
|
175
182
|
await self.tunnel_manager.__aexit__(*sys.exc_info())
|
|
176
183
|
|
|
177
184
|
|
|
178
|
-
FlashManager = synchronize_api(_FlashManager)
|
|
185
|
+
FlashManager = synchronize_api(_FlashManager, target_module=__name__)
|
|
179
186
|
|
|
180
187
|
|
|
181
188
|
@synchronizer.create_blocking
|
|
182
189
|
async def flash_forward(
|
|
183
190
|
port: int,
|
|
184
|
-
process: Optional[subprocess.Popen] = None,
|
|
191
|
+
process: Optional[subprocess.Popen] = None, # to be deprecated
|
|
185
192
|
health_check_url: Optional[str] = None,
|
|
186
193
|
startup_timeout: int = 30,
|
|
194
|
+
exit_grace_period: int = 0,
|
|
187
195
|
h2_enabled: bool = False,
|
|
188
196
|
) -> _FlashManager:
|
|
189
197
|
"""
|
|
@@ -199,6 +207,7 @@ async def flash_forward(
|
|
|
199
207
|
process=process,
|
|
200
208
|
health_check_url=health_check_url,
|
|
201
209
|
startup_timeout=startup_timeout,
|
|
210
|
+
exit_grace_period=exit_grace_period,
|
|
202
211
|
h2_enabled=h2_enabled,
|
|
203
212
|
)
|
|
204
213
|
await manager._start()
|
|
@@ -692,3 +701,33 @@ def _http_server(
|
|
|
692
701
|
|
|
693
702
|
|
|
694
703
|
http_server = synchronize_api(_http_server, target_module=__name__)
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
class _FlashContainerEntry:
|
|
707
|
+
"""
|
|
708
|
+
A class that manages the lifecycle of Flash manager for Flash containers.
|
|
709
|
+
|
|
710
|
+
It is intentional that stop() runs before exit handlers and close().
|
|
711
|
+
This ensures the container is deregistered first, preventing new requests from being routed to it
|
|
712
|
+
while exit handlers execute and the exit grace period elapses, before finally closing the tunnel.
|
|
713
|
+
"""
|
|
714
|
+
|
|
715
|
+
def __init__(self, http_config: api_pb2.HTTPConfig):
|
|
716
|
+
self.http_config: api_pb2.HTTPConfig = http_config
|
|
717
|
+
self.flash_manager: Optional[FlashManager] = None # type: ignore
|
|
718
|
+
|
|
719
|
+
def enter(self):
|
|
720
|
+
if self.http_config != api_pb2.HTTPConfig():
|
|
721
|
+
self.flash_manager = flash_forward(
|
|
722
|
+
self.http_config.port,
|
|
723
|
+
startup_timeout=self.http_config.startup_timeout,
|
|
724
|
+
exit_grace_period=self.http_config.exit_grace_period,
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
def stop(self):
|
|
728
|
+
if self.flash_manager:
|
|
729
|
+
self.flash_manager.stop()
|
|
730
|
+
|
|
731
|
+
def close(self):
|
|
732
|
+
if self.flash_manager:
|
|
733
|
+
self.flash_manager.close()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import modal.client
|
|
2
|
+
import modal_proto.api_pb2
|
|
2
3
|
import subprocess
|
|
3
4
|
import typing
|
|
4
5
|
import typing_extensions
|
|
@@ -11,6 +12,7 @@ class _FlashManager:
|
|
|
11
12
|
process: typing.Optional[subprocess.Popen] = None,
|
|
12
13
|
health_check_url: typing.Optional[str] = None,
|
|
13
14
|
startup_timeout: int = 30,
|
|
15
|
+
exit_grace_period: int = 0,
|
|
14
16
|
h2_enabled: bool = False,
|
|
15
17
|
):
|
|
16
18
|
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
@@ -39,6 +41,7 @@ class FlashManager:
|
|
|
39
41
|
process: typing.Optional[subprocess.Popen] = None,
|
|
40
42
|
health_check_url: typing.Optional[str] = None,
|
|
41
43
|
startup_timeout: int = 30,
|
|
44
|
+
exit_grace_period: int = 0,
|
|
42
45
|
h2_enabled: bool = False,
|
|
43
46
|
): ...
|
|
44
47
|
|
|
@@ -97,6 +100,7 @@ class __flash_forward_spec(typing_extensions.Protocol):
|
|
|
97
100
|
process: typing.Optional[subprocess.Popen] = None,
|
|
98
101
|
health_check_url: typing.Optional[str] = None,
|
|
99
102
|
startup_timeout: int = 30,
|
|
103
|
+
exit_grace_period: int = 0,
|
|
100
104
|
h2_enabled: bool = False,
|
|
101
105
|
) -> FlashManager:
|
|
102
106
|
"""Forward a port to the Modal Flash service, exposing that port as a stable web endpoint.
|
|
@@ -112,6 +116,7 @@ class __flash_forward_spec(typing_extensions.Protocol):
|
|
|
112
116
|
process: typing.Optional[subprocess.Popen] = None,
|
|
113
117
|
health_check_url: typing.Optional[str] = None,
|
|
114
118
|
startup_timeout: int = 30,
|
|
119
|
+
exit_grace_period: int = 0,
|
|
115
120
|
h2_enabled: bool = False,
|
|
116
121
|
) -> FlashManager:
|
|
117
122
|
"""Forward a port to the Modal Flash service, exposing that port as a stable web endpoint.
|
|
@@ -420,3 +425,18 @@ def http_server(
|
|
|
420
425
|
exit_grace_period: The time to wait for the HTTP server to exit gracefully.
|
|
421
426
|
"""
|
|
422
427
|
...
|
|
428
|
+
|
|
429
|
+
class _FlashContainerEntry:
|
|
430
|
+
"""A class that manages the lifecycle of Flash manager for Flash containers.
|
|
431
|
+
|
|
432
|
+
It is intentional that stop() runs before exit handlers and close().
|
|
433
|
+
This ensures the container is deregistered first, preventing new requests from being routed to it
|
|
434
|
+
while exit handlers execute and the exit grace period elapses, before finally closing the tunnel.
|
|
435
|
+
"""
|
|
436
|
+
def __init__(self, http_config: modal_proto.api_pb2.HTTPConfig):
|
|
437
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
438
|
+
...
|
|
439
|
+
|
|
440
|
+
def enter(self): ...
|
|
441
|
+
def stop(self): ...
|
|
442
|
+
def close(self): ...
|
|
@@ -2793,6 +2793,11 @@ message Sandbox {
|
|
|
2793
2793
|
// Exec commands for the sandbox will be issued directly to the sandbox
|
|
2794
2794
|
// command router running on the Modal worker.
|
|
2795
2795
|
bool direct_sandbox_commands_enabled = 34;
|
|
2796
|
+
|
|
2797
|
+
// Internal: restricts sandbox to run on this specific instance type.
|
|
2798
|
+
// Set by server during SandboxRestore to ensure the restored sandbox runs
|
|
2799
|
+
// on the same instance type as the original snapshot.
|
|
2800
|
+
string _restore_instance_type = 35;
|
|
2796
2801
|
}
|
|
2797
2802
|
|
|
2798
2803
|
message SandboxCreateConnectTokenRequest {
|