modal 1.1.5.dev1__tar.gz → 1.1.5.dev3__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.1.5.dev1 → modal-1.1.5.dev3}/PKG-INFO +1 -1
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/client.pyi +2 -2
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/container_process.py +2 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/experimental/flash.py +37 -30
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/experimental/flash.pyi +11 -5
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/functions.pyi +6 -6
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/io_streams.py +4 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/sandbox.py +4 -1
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_version/__init__.py +1 -1
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/LICENSE +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/README.md +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/__init__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/__main__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_clustered_functions.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_clustered_functions.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_container_entrypoint.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_functions.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_ipython.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_location.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_object.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_output.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_partial_function.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_pty.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_resolver.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_resources.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_runtime/__init__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_runtime/asgi.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_runtime/execution_context.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_runtime/telemetry.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_serialization.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_traceback.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_tunnel.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_tunnel.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_type_manager.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/__init__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/app_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/async_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/blob_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/deprecation.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/docker_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/function_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/git_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/hash_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/http_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/logger.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/mount_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/name_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/package_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/shell_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_utils/time_utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_vendor/__init__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_vendor/tblib.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/_watcher.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/app.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/app.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/builder/2023.12.txt +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/builder/2024.04.txt +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/builder/2024.10.txt +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/builder/2025.06.txt +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/builder/README.md +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/builder/base-images.json +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/call_graph.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/__init__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/_download.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/_traceback.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/app.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/cluster.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/config.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/container.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/dict.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/entry_point.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/environment.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/import_refs.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/launch.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/network_file_system.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/profile.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/programs/__init__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/programs/launch_instance_ssh.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/programs/run_marimo.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/programs/vscode.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/queues.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/run.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/secret.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/token.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/utils.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cli/volume.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/client.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cls.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/cls.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/config.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/container_process.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/dict.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/dict.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/environments.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/environments.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/exception.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/experimental/__init__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/experimental/ipython.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/file_io.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/file_io.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/file_pattern_matcher.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/functions.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/gpu.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/image.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/image.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/io_streams.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/mount.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/mount.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/network_file_system.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/network_file_system.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/object.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/object.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/output.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/parallel_map.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/parallel_map.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/partial_function.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/partial_function.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/proxy.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/proxy.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/py.typed +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/queue.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/queue.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/retries.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/runner.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/runner.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/running_app.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/sandbox.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/schedule.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/scheduler_placement.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/secret.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/secret.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/serving.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/serving.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/snapshot.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/snapshot.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/stream_type.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/token_flow.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/token_flow.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/volume.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal/volume.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal.egg-info/requires.txt +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal.egg-info/top_level.txt +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_docs/__init__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/__init__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/api.proto +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/api_grpc.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/api_pb2.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/api_pb2.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/modal_options_grpc.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/options.proto +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/options_grpc.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/options_pb2.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/options_pb2.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/options_pb2_grpc.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/options_pb2_grpc.pyi +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_proto/py.typed +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/modal_version/__main__.py +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/pyproject.toml +0 -0
- {modal-1.1.5.dev1 → modal-1.1.5.dev3}/setup.cfg +0 -0
@@ -33,7 +33,7 @@ class _Client:
|
|
33
33
|
server_url: str,
|
34
34
|
client_type: int,
|
35
35
|
credentials: typing.Optional[tuple[str, str]],
|
36
|
-
version: str = "1.1.5.
|
36
|
+
version: str = "1.1.5.dev3",
|
37
37
|
):
|
38
38
|
"""mdmd:hidden
|
39
39
|
The Modal client object is not intended to be instantiated directly by users.
|
@@ -164,7 +164,7 @@ class Client:
|
|
164
164
|
server_url: str,
|
165
165
|
client_type: int,
|
166
166
|
credentials: typing.Optional[tuple[str, str]],
|
167
|
-
version: str = "1.1.5.
|
167
|
+
version: str = "1.1.5.dev3",
|
168
168
|
):
|
169
169
|
"""mdmd:hidden
|
170
170
|
The Modal client object is not intended to be instantiated directly by users.
|
@@ -10,6 +10,7 @@ from ._utils.async_utils import TaskContext, synchronize_api
|
|
10
10
|
from ._utils.grpc_utils import retry_transient_errors
|
11
11
|
from ._utils.shell_utils import stream_from_stdin, write_to_fd
|
12
12
|
from .client import _Client
|
13
|
+
from .config import logger
|
13
14
|
from .exception import InteractiveTimeoutError, InvalidError
|
14
15
|
from .io_streams import _StreamReader, _StreamWriter
|
15
16
|
from .stream_type import StreamType
|
@@ -136,6 +137,7 @@ class _ContainerProcess(Generic[T]):
|
|
136
137
|
self._returncode = await asyncio.wait_for(self._wait_for_completion(), timeout=timeout)
|
137
138
|
except (asyncio.TimeoutError, TimeoutError):
|
138
139
|
self._returncode = -1
|
140
|
+
logger.debug(f"ContainerProcess {self._process_id} wait completed with returncode {self._returncode}")
|
139
141
|
return self._returncode
|
140
142
|
|
141
143
|
async def attach(self):
|
@@ -21,7 +21,7 @@ from ..client import _Client
|
|
21
21
|
from ..config import logger
|
22
22
|
from ..exception import InvalidError
|
23
23
|
|
24
|
-
|
24
|
+
_MAX_FAILURES = 3
|
25
25
|
|
26
26
|
|
27
27
|
class _FlashManager:
|
@@ -42,7 +42,9 @@ class _FlashManager:
|
|
42
42
|
self.num_failures = 0
|
43
43
|
self.task_id = os.environ["MODAL_TASK_ID"]
|
44
44
|
|
45
|
-
async def
|
45
|
+
async def is_port_connection_healthy(
|
46
|
+
self, process: Optional[subprocess.Popen], timeout: int = 5
|
47
|
+
) -> tuple[bool, Optional[Exception]]:
|
46
48
|
import socket
|
47
49
|
|
48
50
|
start_time = time.monotonic()
|
@@ -50,13 +52,13 @@ class _FlashManager:
|
|
50
52
|
while time.monotonic() - start_time < timeout:
|
51
53
|
try:
|
52
54
|
if process is not None and process.poll() is not None:
|
53
|
-
return Exception(f"Process {process.pid} exited with code {process.returncode}")
|
55
|
+
return False, Exception(f"Process {process.pid} exited with code {process.returncode}")
|
54
56
|
with socket.create_connection(("localhost", self.port), timeout=1):
|
55
|
-
return
|
57
|
+
return True, None
|
56
58
|
except (ConnectionRefusedError, OSError):
|
57
59
|
await asyncio.sleep(0.1)
|
58
60
|
|
59
|
-
return Exception(f"Waited too long for port {self.port} to start accepting connections")
|
61
|
+
return False, Exception(f"Waited too long for port {self.port} to start accepting connections")
|
60
62
|
|
61
63
|
async def _start(self):
|
62
64
|
self.tunnel = await self.tunnel_manager.__aenter__()
|
@@ -74,7 +76,7 @@ class _FlashManager:
|
|
74
76
|
while True:
|
75
77
|
try:
|
76
78
|
# Check if the container should be drained (e.g., too many failures)
|
77
|
-
if self.num_failures >
|
79
|
+
if self.num_failures > _MAX_FAILURES:
|
78
80
|
logger.warning(
|
79
81
|
f"[Modal Flash] Draining task {self.task_id} on {self.tunnel.url} due to too many failures."
|
80
82
|
)
|
@@ -101,35 +103,38 @@ class _FlashManager:
|
|
101
103
|
first_registration = True
|
102
104
|
while True:
|
103
105
|
try:
|
104
|
-
await self.
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
106
|
+
port_check_resp, port_check_error = await self.is_port_connection_healthy(process=self.process)
|
107
|
+
if port_check_resp:
|
108
|
+
resp = await self.client.stub.FlashContainerRegister(
|
109
|
+
api_pb2.FlashContainerRegisterRequest(
|
110
|
+
priority=10,
|
111
|
+
weight=5,
|
112
|
+
host=host,
|
113
|
+
port=port,
|
114
|
+
),
|
115
|
+
timeout=10,
|
116
|
+
)
|
117
|
+
self.num_failures = 0
|
118
|
+
if first_registration:
|
119
|
+
logger.warning(
|
120
|
+
f"[Modal Flash] Listening at {resp.url} over {self.tunnel.url} for task_id {self.task_id}"
|
121
|
+
)
|
122
|
+
first_registration = False
|
123
|
+
else:
|
124
|
+
logger.error(
|
125
|
+
f"[Modal Flash] Deregistering container {self.task_id} on {self.tunnel.url} "
|
126
|
+
f"due to error: {port_check_error}, num_failures: {self.num_failures}"
|
127
|
+
)
|
128
|
+
self.num_failures += 1
|
129
|
+
await retry_transient_errors(
|
130
|
+
self.client.stub.FlashContainerDeregister,
|
131
|
+
api_pb2.FlashContainerDeregisterRequest(),
|
118
132
|
)
|
119
|
-
first_registration = False
|
120
133
|
except asyncio.CancelledError:
|
121
134
|
logger.warning("[Modal Flash] Shutting down...")
|
122
135
|
break
|
123
136
|
except Exception as e:
|
124
137
|
logger.error(f"[Modal Flash] Heartbeat failed: {e}")
|
125
|
-
self.num_failures += 1
|
126
|
-
logger.error(
|
127
|
-
f"[Modal Flash] Deregistering container {self.tunnel.url}, num_failures: {self.num_failures}"
|
128
|
-
)
|
129
|
-
await retry_transient_errors(
|
130
|
-
self.client.stub.FlashContainerDeregister,
|
131
|
-
api_pb2.FlashContainerDeregisterRequest(),
|
132
|
-
)
|
133
138
|
|
134
139
|
try:
|
135
140
|
await asyncio.sleep(1)
|
@@ -167,7 +172,9 @@ FlashManager = synchronize_api(_FlashManager)
|
|
167
172
|
|
168
173
|
@synchronizer.create_blocking
|
169
174
|
async def flash_forward(
|
170
|
-
port: int,
|
175
|
+
port: int,
|
176
|
+
process: Optional[subprocess.Popen] = None,
|
177
|
+
health_check_url: Optional[str] = None,
|
171
178
|
) -> _FlashManager:
|
172
179
|
"""
|
173
180
|
Forward a port to the Modal Flash service, exposing that port as a stable web endpoint.
|
@@ -15,7 +15,9 @@ class _FlashManager:
|
|
15
15
|
"""Initialize self. See help(type(self)) for accurate signature."""
|
16
16
|
...
|
17
17
|
|
18
|
-
async def
|
18
|
+
async def is_port_connection_healthy(
|
19
|
+
self, process: typing.Optional[subprocess.Popen], timeout: int = 5
|
20
|
+
) -> tuple[bool, typing.Optional[Exception]]: ...
|
19
21
|
async def _start(self): ...
|
20
22
|
async def _drain_container(self):
|
21
23
|
"""Background task that checks if we've encountered too many failures and drains the container if so."""
|
@@ -37,11 +39,15 @@ class FlashManager:
|
|
37
39
|
health_check_url: typing.Optional[str] = None,
|
38
40
|
): ...
|
39
41
|
|
40
|
-
class
|
41
|
-
def __call__(
|
42
|
-
|
42
|
+
class __is_port_connection_healthy_spec(typing_extensions.Protocol[SUPERSELF]):
|
43
|
+
def __call__(
|
44
|
+
self, /, process: typing.Optional[subprocess.Popen], timeout: int = 5
|
45
|
+
) -> tuple[bool, typing.Optional[Exception]]: ...
|
46
|
+
async def aio(
|
47
|
+
self, /, process: typing.Optional[subprocess.Popen], timeout: int = 5
|
48
|
+
) -> tuple[bool, typing.Optional[Exception]]: ...
|
43
49
|
|
44
|
-
|
50
|
+
is_port_connection_healthy: __is_port_connection_healthy_spec[typing_extensions.Self]
|
45
51
|
|
46
52
|
class ___start_spec(typing_extensions.Protocol[SUPERSELF]):
|
47
53
|
def __call__(self, /): ...
|
@@ -445,7 +445,7 @@ class Function(
|
|
445
445
|
|
446
446
|
_call_generator: ___call_generator_spec[typing_extensions.Self]
|
447
447
|
|
448
|
-
class __remote_spec(typing_extensions.Protocol[
|
448
|
+
class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
449
449
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
|
450
450
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
451
451
|
...
|
@@ -454,7 +454,7 @@ class Function(
|
|
454
454
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
455
455
|
...
|
456
456
|
|
457
|
-
remote: __remote_spec[modal._functions.
|
457
|
+
remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
458
458
|
|
459
459
|
class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
|
460
460
|
def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
|
@@ -481,7 +481,7 @@ class Function(
|
|
481
481
|
"""
|
482
482
|
...
|
483
483
|
|
484
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
484
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
485
485
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
486
486
|
"""[Experimental] Calls the function with the given arguments, without waiting for the results.
|
487
487
|
|
@@ -505,7 +505,7 @@ class Function(
|
|
505
505
|
...
|
506
506
|
|
507
507
|
_experimental_spawn: ___experimental_spawn_spec[
|
508
|
-
modal._functions.
|
508
|
+
modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
|
509
509
|
]
|
510
510
|
|
511
511
|
class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
|
@@ -514,7 +514,7 @@ class Function(
|
|
514
514
|
|
515
515
|
_spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
|
516
516
|
|
517
|
-
class __spawn_spec(typing_extensions.Protocol[
|
517
|
+
class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
518
518
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
519
519
|
"""Calls the function with the given arguments, without waiting for the results.
|
520
520
|
|
@@ -535,7 +535,7 @@ class Function(
|
|
535
535
|
"""
|
536
536
|
...
|
537
537
|
|
538
|
-
spawn: __spawn_spec[modal._functions.
|
538
|
+
spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
539
539
|
|
540
540
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
|
541
541
|
"""Return the inner Python object wrapped by this Modal Function."""
|
@@ -21,6 +21,7 @@ from modal_proto import api_pb2
|
|
21
21
|
from ._utils.async_utils import synchronize_api
|
22
22
|
from ._utils.grpc_utils import RETRYABLE_GRPC_STATUS_CODES, retry_transient_errors
|
23
23
|
from .client import _Client
|
24
|
+
from .config import logger
|
24
25
|
from .stream_type import StreamType
|
25
26
|
|
26
27
|
if TYPE_CHECKING:
|
@@ -180,6 +181,7 @@ class _StreamReader(Generic[T]):
|
|
180
181
|
"""
|
181
182
|
data_str = ""
|
182
183
|
data_bytes = b""
|
184
|
+
logger.debug(f"{self._object_id} StreamReader fd={self._file_descriptor} read starting")
|
183
185
|
async for message in self._get_logs():
|
184
186
|
if message is None:
|
185
187
|
break
|
@@ -188,6 +190,7 @@ class _StreamReader(Generic[T]):
|
|
188
190
|
else:
|
189
191
|
data_bytes += message
|
190
192
|
|
193
|
+
logger.debug(f"{self._object_id} StreamReader fd={self._file_descriptor} read completed after EOF")
|
191
194
|
if self._text:
|
192
195
|
return cast(T, data_str)
|
193
196
|
else:
|
@@ -232,6 +235,7 @@ class _StreamReader(Generic[T]):
|
|
232
235
|
elif isinstance(exc, ClientClosed):
|
233
236
|
# If the client was closed, the user has triggered a cleanup.
|
234
237
|
break
|
238
|
+
logger.error(f"{self._object_id} stream read failure while consuming process output: {exc}")
|
235
239
|
raise exc
|
236
240
|
|
237
241
|
async def _stream_container_process(self) -> AsyncGenerator[tuple[Optional[bytes], str], None]:
|
@@ -5,6 +5,8 @@ import time
|
|
5
5
|
from collections.abc import AsyncGenerator, Sequence
|
6
6
|
from typing import TYPE_CHECKING, AsyncIterator, Literal, Optional, Union, overload
|
7
7
|
|
8
|
+
from .config import config, logger
|
9
|
+
|
8
10
|
if TYPE_CHECKING:
|
9
11
|
import _typeshed
|
10
12
|
|
@@ -26,7 +28,6 @@ from ._utils.grpc_utils import retry_transient_errors
|
|
26
28
|
from ._utils.mount_utils import validate_network_file_systems, validate_volumes
|
27
29
|
from ._utils.name_utils import is_valid_object_name
|
28
30
|
from .client import _Client
|
29
|
-
from .config import config
|
30
31
|
from .container_process import _ContainerProcess
|
31
32
|
from .exception import AlreadyExistsError, ExecutionError, InvalidError, SandboxTerminatedError, SandboxTimeoutError
|
32
33
|
from .file_io import FileWatchEvent, FileWatchEventType, _FileIO
|
@@ -588,6 +589,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
588
589
|
req = api_pb2.SandboxWaitRequest(sandbox_id=self.object_id, timeout=10)
|
589
590
|
resp = await retry_transient_errors(self._client.stub.SandboxWait, req)
|
590
591
|
if resp.result.status:
|
592
|
+
logger.debug(f"Sandbox {self.object_id} wait completed with status {resp.result.status}")
|
591
593
|
self._result = resp.result
|
592
594
|
|
593
595
|
if resp.result.status == api_pb2.GenericResult.GENERIC_STATUS_TIMEOUT:
|
@@ -757,6 +759,7 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
757
759
|
resp = await retry_transient_errors(self._client.stub.ContainerExec, req)
|
758
760
|
by_line = bufsize == 1
|
759
761
|
exec_deadline = time.monotonic() + int(timeout) + CONTAINER_EXEC_TIMEOUT_BUFFER if timeout else None
|
762
|
+
logger.debug(f"Created ContainerProcess for exec_id {resp.exec_id} on Sandbox {self.object_id}")
|
760
763
|
return _ContainerProcess(
|
761
764
|
resp.exec_id,
|
762
765
|
self._client,
|
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
|