modal 1.2.5.dev14__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.dev14 → modal-1.2.5.dev15}/PKG-INFO +1 -1
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_partial_function.py +1 -2
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_runtime/user_code_imports.py +5 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/app.py +7 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/client.pyi +2 -2
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/experimental/flash.py +43 -4
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/experimental/flash.pyi +20 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/functions.pyi +6 -6
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_version/__init__.py +1 -1
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/LICENSE +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/README.md +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/__init__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/__main__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_billing.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_clustered_functions.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_clustered_functions.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_container_entrypoint.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_functions.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_grpc_client.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_ipython.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_load_context.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_location.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_object.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_output.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_pty.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_resolver.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_resources.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_runtime/__init__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_runtime/asgi.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_runtime/execution_context.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_runtime/telemetry.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_serialization.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_traceback.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_tunnel.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_tunnel.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_type_manager.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/__init__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/app_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/async_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/blob_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/deprecation.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/docker_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/function_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/git_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/hash_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/http_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/logger.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/mount_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/name_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/package_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/shell_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_utils/time_utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_vendor/__init__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_vendor/tblib.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/_watcher.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/app.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/billing.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/builder/2023.12.txt +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/builder/2024.04.txt +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/builder/2024.10.txt +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/builder/2025.06.txt +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/builder/README.md +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/builder/base-images.json +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/call_graph.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/__init__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/_download.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/_traceback.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/app.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/cluster.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/config.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/container.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/dict.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/entry_point.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/environment.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/import_refs.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/launch.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/network_file_system.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/profile.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/programs/__init__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/programs/launch_instance_ssh.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/programs/run_marimo.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/programs/vscode.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/queues.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/run.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/secret.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/shell.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/token.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/utils.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cli/volume.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/client.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cls.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/cls.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/config.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/container_process.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/container_process.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/dict.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/dict.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/environments.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/environments.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/exception.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/experimental/__init__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/experimental/ipython.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/file_io.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/file_io.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/file_pattern_matcher.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/functions.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/gpu.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/image.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/image.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/io_streams.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/io_streams.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/mount.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/mount.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/network_file_system.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/network_file_system.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/object.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/object.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/output.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/parallel_map.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/parallel_map.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/partial_function.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/partial_function.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/proxy.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/proxy.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/py.typed +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/queue.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/queue.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/retries.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/runner.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/runner.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/running_app.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/sandbox.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/sandbox.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/schedule.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/scheduler_placement.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/secret.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/secret.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/serving.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/serving.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/snapshot.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/snapshot.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/stream_type.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/token_flow.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/token_flow.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/volume.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal/volume.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal.egg-info/requires.txt +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal.egg-info/top_level.txt +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_docs/__init__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/__init__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/api.proto +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/api_grpc.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/api_pb2.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/api_pb2.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/py.typed +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/sandbox_router.proto +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/sandbox_router_grpc.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2_grpc.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2_grpc.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/task_command_router.proto +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/modal_version/__main__.py +0 -0
- {modal-1.2.5.dev14 → modal-1.2.5.dev15}/pyproject.toml +0 -0
- {modal-1.2.5.dev14 → 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): ...
|
|
@@ -409,7 +409,7 @@ class Function(
|
|
|
409
409
|
|
|
410
410
|
_call_generator: ___call_generator_spec[typing_extensions.Self]
|
|
411
411
|
|
|
412
|
-
class __remote_spec(typing_extensions.Protocol[
|
|
412
|
+
class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
413
413
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
|
|
414
414
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
415
415
|
...
|
|
@@ -418,7 +418,7 @@ class Function(
|
|
|
418
418
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
419
419
|
...
|
|
420
420
|
|
|
421
|
-
remote: __remote_spec[modal._functions.
|
|
421
|
+
remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
|
422
422
|
|
|
423
423
|
class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
424
424
|
def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
|
|
@@ -445,7 +445,7 @@ class Function(
|
|
|
445
445
|
"""
|
|
446
446
|
...
|
|
447
447
|
|
|
448
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
|
448
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
449
449
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
450
450
|
"""[Experimental] Calls the function with the given arguments, without waiting for the results.
|
|
451
451
|
|
|
@@ -469,7 +469,7 @@ class Function(
|
|
|
469
469
|
...
|
|
470
470
|
|
|
471
471
|
_experimental_spawn: ___experimental_spawn_spec[
|
|
472
|
-
modal._functions.
|
|
472
|
+
modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
|
|
473
473
|
]
|
|
474
474
|
|
|
475
475
|
class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
|
|
@@ -478,7 +478,7 @@ class Function(
|
|
|
478
478
|
|
|
479
479
|
_spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
|
|
480
480
|
|
|
481
|
-
class __spawn_spec(typing_extensions.Protocol[
|
|
481
|
+
class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
|
482
482
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
483
483
|
"""Calls the function with the given arguments, without waiting for the results.
|
|
484
484
|
|
|
@@ -499,7 +499,7 @@ class Function(
|
|
|
499
499
|
"""
|
|
500
500
|
...
|
|
501
501
|
|
|
502
|
-
spawn: __spawn_spec[modal._functions.
|
|
502
|
+
spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
|
503
503
|
|
|
504
504
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
|
|
505
505
|
"""Return the inner Python object wrapped by this Modal Function."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|