modal 1.4.4.dev14__tar.gz → 1.4.4.dev16__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.4.dev14 → modal-1.4.4.dev16}/PKG-INFO +1 -1
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_container_entrypoint.py +158 -95
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_functions.py +1 -1
- modal-1.4.4.dev14/modal/image.py → modal-1.4.4.dev16/modal/_image.py +17 -21
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/execution_context.py +3 -2
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/task_lifecycle_manager.py +24 -1
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/task_lifecycle_manager.pyi +28 -4
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/app.py +1 -1
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/app.pyi +9 -8
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/entry_point.py +2 -0
- modal-1.4.4.dev16/modal/cli/skills.py +310 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/client.pyi +2 -2
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/experimental/__init__.py +6 -6
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/functions.pyi +6 -6
- modal-1.4.4.dev16/modal/image.py +5 -0
- modal-1.4.4.dev16/modal/image.pyi +1368 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/sandbox.py +1 -1
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/sandbox.pyi +11 -10
- modal-1.4.4.dev16/modal/skills/modal/SKILL.md +86 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/SOURCES.txt +3 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/api_pb2.py +1010 -1010
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/api_pb2.pyi +39 -5
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_version/__init__.py +1 -1
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/pyproject.toml +1 -1
- modal-1.4.4.dev14/modal/image.pyi +0 -2748
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/LICENSE +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/README.md +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/__init__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/__main__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_billing.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_clustered_functions.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_clustered_functions.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_environments.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_function_variants.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_grpc_client.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_ipython.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_load_context.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_location.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_logs.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_object.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_output/__init__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_output/manager.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_output/pty.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_output/rich.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_output/status.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_partial_function.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_resolver.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_resources.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/__init__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/asgi.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/telemetry.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_serialization.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_server.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_traceback.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_tunnel.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_tunnel.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_type_manager.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/__init__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/app_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/async_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/blob_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/browser_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/deprecation.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/docker_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/function_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/git_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/hash_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/http_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/logger.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/mount_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/name_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/package_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/shell_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/time_utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_vendor/__init__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_vendor/tblib.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_vendor/version.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_watcher.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_workspace.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/billing.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/2023.12.txt +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/2024.04.txt +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/2024.10.txt +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/2025.06.txt +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/README.md +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/base-images.json +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/call_graph.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/__init__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/_download.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/_help.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/_traceback.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/app.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/billing.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/bootstrap.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/changelog.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/cluster.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/config.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/container.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/dashboard.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/dict.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/environment.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/import_refs.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/launch.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/logo.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/network_file_system.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/profile.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/programs/__init__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/programs/vscode.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/queues.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/run.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/secret.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/selector.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/shell.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/token.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/utils.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/volume.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/client.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cls.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cls.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/config.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/container_process.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/container_process.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/dict.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/dict.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/environments.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/environments.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/exception.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/experimental/flash.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/experimental/flash.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/experimental/ipython.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/file_io.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/file_io.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/file_pattern_matcher.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/functions.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/io_streams.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/io_streams.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/mount.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/mount.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/network_file_system.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/network_file_system.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/object.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/object.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/output.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/parallel_map.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/parallel_map.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/partial_function.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/partial_function.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/proxy.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/proxy.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/py.typed +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/queue.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/queue.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/retries.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/runner.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/runner.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/running_app.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/sandbox_fs.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/sandbox_fs.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/schedule.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/scheduler_placement.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/secret.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/secret.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/server.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/server.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/serving.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/serving.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/snapshot.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/snapshot.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/stream_type.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/token_flow.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/token_flow.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/volume.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/volume.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/workspace.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/workspace.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/requires.txt +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/top_level.txt +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/__init__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/mdmd/types.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/__init__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/api_grpc.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/py.typed +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_version/__main__.py +0 -0
- {modal-1.4.4.dev14 → modal-1.4.4.dev16}/setup.cfg +0 -0
|
@@ -14,7 +14,6 @@ if telemetry_socket:
|
|
|
14
14
|
from ._runtime.telemetry import instrument_imports
|
|
15
15
|
|
|
16
16
|
instrument_imports(telemetry_socket)
|
|
17
|
-
|
|
18
17
|
import asyncio
|
|
19
18
|
import queue
|
|
20
19
|
import signal
|
|
@@ -41,10 +40,11 @@ from ._runtime.container_io_manager import (
|
|
|
41
40
|
ContainerIOManager,
|
|
42
41
|
IOContext,
|
|
43
42
|
)
|
|
44
|
-
from ._runtime.task_lifecycle_manager import UserException
|
|
43
|
+
from ._runtime.task_lifecycle_manager import TaskLifecycleManager, UserException
|
|
45
44
|
|
|
46
45
|
if TYPE_CHECKING:
|
|
47
46
|
import modal._runtime.container_io_manager
|
|
47
|
+
import modal._runtime.task_lifecycle_manager
|
|
48
48
|
import modal._runtime.user_code_imports
|
|
49
49
|
|
|
50
50
|
|
|
@@ -287,12 +287,146 @@ def get_serialized_user_class_and_function(
|
|
|
287
287
|
return ser_usr_cls, ser_fun
|
|
288
288
|
|
|
289
289
|
|
|
290
|
-
def
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
290
|
+
def get_server_service(
|
|
291
|
+
container_args: api_pb2.ContainerArguments, function_def: api_pb2.Function, _client: _Client, client: Client
|
|
292
|
+
) -> Service:
|
|
293
|
+
ser_usr_cls, ser_fun = get_serialized_user_class_and_function(function_def, _client)
|
|
294
|
+
service_base_function_id = container_args.app_layout.function_ids[function_def.function_name]
|
|
295
|
+
service_function_hydration_data = [
|
|
296
|
+
o for o in container_args.app_layout.objects if o.object_id == service_base_function_id
|
|
297
|
+
][0]
|
|
298
|
+
service = import_server_service(
|
|
299
|
+
function_def,
|
|
300
|
+
service_function_hydration_data,
|
|
301
|
+
client,
|
|
302
|
+
ser_usr_cls,
|
|
303
|
+
)
|
|
304
|
+
return service
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def get_class_service(
|
|
308
|
+
container_args: api_pb2.ContainerArguments, function_def: api_pb2.Function, _client: _Client, client: Client
|
|
309
|
+
) -> Service:
|
|
310
|
+
ser_usr_cls, ser_fun = get_serialized_user_class_and_function(function_def, _client)
|
|
311
|
+
if container_args.serialized_params:
|
|
312
|
+
param_args, param_kwargs = deserialize_params(container_args.serialized_params, function_def, _client)
|
|
313
|
+
else:
|
|
314
|
+
param_args = ()
|
|
315
|
+
param_kwargs = {}
|
|
316
|
+
# this is a bit ugly - match the function and class based on function name to get metadata
|
|
317
|
+
# This metadata is required in order to hydrate the class in case it's not globally
|
|
318
|
+
# decorated (or serialized)
|
|
319
|
+
service_base_function_id = container_args.app_layout.function_ids[function_def.function_name]
|
|
320
|
+
service_function_hydration_data = [
|
|
321
|
+
o for o in container_args.app_layout.objects if o.object_id == service_base_function_id
|
|
322
|
+
][0]
|
|
323
|
+
class_id = container_args.app_layout.class_ids[function_def.function_name.removesuffix(".*")]
|
|
324
|
+
|
|
325
|
+
service = import_class_service(
|
|
326
|
+
function_def,
|
|
327
|
+
service_function_hydration_data,
|
|
328
|
+
class_id,
|
|
329
|
+
client,
|
|
330
|
+
ser_usr_cls,
|
|
331
|
+
param_args,
|
|
332
|
+
param_kwargs,
|
|
333
|
+
)
|
|
334
|
+
return service
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def get_function_service(function_def: api_pb2.Function, _client: _Client) -> Service:
|
|
338
|
+
ser_usr_cls, ser_fun = get_serialized_user_class_and_function(function_def, _client)
|
|
339
|
+
assert ser_usr_cls is None
|
|
340
|
+
service = import_single_function_service(
|
|
341
|
+
function_def,
|
|
342
|
+
ser_fun,
|
|
343
|
+
)
|
|
344
|
+
return service
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def hydrate_function(
|
|
348
|
+
container_args: api_pb2.ContainerArguments,
|
|
349
|
+
task_lifecycle_manager: TaskLifecycleManager,
|
|
350
|
+
function_def: api_pb2.Function,
|
|
351
|
+
_client: _Client,
|
|
352
|
+
client: Client,
|
|
353
|
+
) -> Service:
|
|
294
354
|
active_app: _App
|
|
295
355
|
service: Service
|
|
356
|
+
with task_lifecycle_manager.handle_task_lifecycle_exception():
|
|
357
|
+
with execution_context._import_context():
|
|
358
|
+
if function_def.is_server:
|
|
359
|
+
service = get_server_service(container_args, function_def, _client, client)
|
|
360
|
+
elif function_def.is_class:
|
|
361
|
+
service = get_class_service(container_args, function_def, _client, client)
|
|
362
|
+
else:
|
|
363
|
+
service = get_function_service(function_def, _client)
|
|
364
|
+
|
|
365
|
+
active_app = service.app
|
|
366
|
+
|
|
367
|
+
# Get ids and metadata for objects (primarily functions and classes) on the app
|
|
368
|
+
container_app: RunningApp = running_app_from_layout(container_args.app_id, container_args.app_layout)
|
|
369
|
+
|
|
370
|
+
# Initialize objects on the app.
|
|
371
|
+
# This is basically only functions and classes - anything else is deprecated and will be unsupported soon
|
|
372
|
+
app: App = cast(App, synchronizer._translate_out(active_app))
|
|
373
|
+
app._init_container(client, container_app)
|
|
374
|
+
|
|
375
|
+
# Hydrate all function dependencies.
|
|
376
|
+
# TODO(erikbern): we an remove this once we
|
|
377
|
+
# 1. Enable lazy hydration for all objects
|
|
378
|
+
# 2. Fully deprecate .new() objects
|
|
379
|
+
if service.service_deps is not None: # this is not set for serialized or non-global scope functions
|
|
380
|
+
dep_object_ids: list[str] = [dep.object_id for dep in function_def.object_dependencies]
|
|
381
|
+
if len(service.service_deps) != len(dep_object_ids):
|
|
382
|
+
raise ExecutionError(
|
|
383
|
+
f"Function has {len(service.service_deps)} dependencies"
|
|
384
|
+
f" but container got {len(dep_object_ids)} object ids.\n"
|
|
385
|
+
f"Code deps: {service.service_deps}\n"
|
|
386
|
+
f"Object ids: {dep_object_ids}\n"
|
|
387
|
+
"\n"
|
|
388
|
+
"This can happen if you are defining Modal objects under a conditional statement "
|
|
389
|
+
"that evaluates differently in the local and remote environments."
|
|
390
|
+
)
|
|
391
|
+
for object_id, obj in zip(dep_object_ids, service.service_deps):
|
|
392
|
+
metadata: Message | None = container_app.object_handle_metadata[object_id]
|
|
393
|
+
obj._hydrate(object_id, _client, metadata)
|
|
394
|
+
|
|
395
|
+
return service
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def run_server(container_args: api_pb2.ContainerArguments, client: Client):
|
|
399
|
+
function_def = container_args.function_def
|
|
400
|
+
_client: _Client = cast(_Client, synchronizer._translate_in(client)) # TODO(erikbern): ugly
|
|
401
|
+
task_lifecycle_manager = TaskLifecycleManager(
|
|
402
|
+
container_args.task_id,
|
|
403
|
+
container_args.function_id,
|
|
404
|
+
container_args.function_def,
|
|
405
|
+
container_args.checkpoint_id or None,
|
|
406
|
+
client,
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
service: Service = hydrate_function(container_args, task_lifecycle_manager, function_def, _client, client)
|
|
410
|
+
# Initialize clustered functions.
|
|
411
|
+
if function_def._experimental_group_size > 0:
|
|
412
|
+
initialize_clustered_function(
|
|
413
|
+
client,
|
|
414
|
+
container_args.task_id,
|
|
415
|
+
function_def._experimental_group_size,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
with UserCodeEventLoop() as event_loop:
|
|
419
|
+
with service.lifecycle_context(
|
|
420
|
+
event_loop,
|
|
421
|
+
task_lifecycle_manager=task_lifecycle_manager,
|
|
422
|
+
):
|
|
423
|
+
call_server(event_loop)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def run_function(container_args: api_pb2.ContainerArguments, client: Client):
|
|
427
|
+
# This is a bit weird but we need both the blocking and async versions of ContainerIOManager.
|
|
428
|
+
# At some point, we should fix that by having built-in support for running "user code"
|
|
429
|
+
container_io_manager: Any = ContainerIOManager(container_args, client)
|
|
296
430
|
function_def = container_args.function_def
|
|
297
431
|
# The worker sets this flag to "1" for snapshot and restore tasks. Otherwise, this flag is unset,
|
|
298
432
|
# in which case snapshots should be disabled.
|
|
@@ -306,92 +440,17 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
306
440
|
container_io_manager.hello()
|
|
307
441
|
|
|
308
442
|
with container_io_manager.heartbeats(is_snapshotting_function), UserCodeEventLoop() as event_loop:
|
|
309
|
-
# If this is a serialized function, fetch the definition from the server
|
|
310
|
-
ser_usr_cls, ser_fun = get_serialized_user_class_and_function(function_def, _client)
|
|
311
|
-
|
|
312
443
|
# Initialize the function, importing user code.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
service_function_hydration_data = [
|
|
324
|
-
o for o in container_args.app_layout.objects if o.object_id == service_base_function_id
|
|
325
|
-
][0]
|
|
326
|
-
service = import_server_service(
|
|
327
|
-
function_def,
|
|
328
|
-
service_function_hydration_data,
|
|
329
|
-
client,
|
|
330
|
-
ser_usr_cls,
|
|
331
|
-
)
|
|
332
|
-
elif function_def.is_class:
|
|
333
|
-
# this is a bit ugly - match the function and class based on function name to get metadata
|
|
334
|
-
# This metadata is required in order to hydrate the class in case it's not globally
|
|
335
|
-
# decorated (or serialized)
|
|
336
|
-
service_base_function_id = container_args.app_layout.function_ids[function_def.function_name]
|
|
337
|
-
service_function_hydration_data = [
|
|
338
|
-
o for o in container_args.app_layout.objects if o.object_id == service_base_function_id
|
|
339
|
-
][0]
|
|
340
|
-
class_id = container_args.app_layout.class_ids[function_def.function_name.removesuffix(".*")]
|
|
341
|
-
|
|
342
|
-
service = import_class_service(
|
|
343
|
-
function_def,
|
|
344
|
-
service_function_hydration_data,
|
|
345
|
-
class_id,
|
|
346
|
-
client,
|
|
347
|
-
ser_usr_cls,
|
|
348
|
-
param_args,
|
|
349
|
-
param_kwargs,
|
|
350
|
-
)
|
|
351
|
-
else:
|
|
352
|
-
assert ser_usr_cls is None
|
|
353
|
-
service = import_single_function_service(
|
|
354
|
-
function_def,
|
|
355
|
-
ser_fun,
|
|
356
|
-
)
|
|
357
|
-
|
|
358
|
-
active_app = service.app
|
|
359
|
-
|
|
360
|
-
if function_def.pty_info.pty_type == api_pb2.PTYInfo.PTY_TYPE_SHELL:
|
|
361
|
-
# Concurrency and batching doesn't apply for `modal shell`.
|
|
362
|
-
batch_max_size = 0
|
|
363
|
-
batch_wait_ms = 0
|
|
364
|
-
else:
|
|
365
|
-
batch_max_size = function_def.batch_max_size or 0
|
|
366
|
-
batch_wait_ms = function_def.batch_linger_ms or 0
|
|
367
|
-
|
|
368
|
-
# Get ids and metadata for objects (primarily functions and classes) on the app
|
|
369
|
-
container_app: RunningApp = running_app_from_layout(container_args.app_id, container_args.app_layout)
|
|
370
|
-
|
|
371
|
-
# Initialize objects on the app.
|
|
372
|
-
# This is basically only functions and classes - anything else is deprecated and will be unsupported soon
|
|
373
|
-
app: App = cast(App, synchronizer._translate_out(active_app))
|
|
374
|
-
app._init_container(client, container_app)
|
|
375
|
-
|
|
376
|
-
# Hydrate all function dependencies.
|
|
377
|
-
# TODO(erikbern): we an remove this once we
|
|
378
|
-
# 1. Enable lazy hydration for all objects
|
|
379
|
-
# 2. Fully deprecate .new() objects
|
|
380
|
-
if service.service_deps is not None: # this is not set for serialized or non-global scope functions
|
|
381
|
-
dep_object_ids: list[str] = [dep.object_id for dep in function_def.object_dependencies]
|
|
382
|
-
if len(service.service_deps) != len(dep_object_ids):
|
|
383
|
-
raise ExecutionError(
|
|
384
|
-
f"Function has {len(service.service_deps)} dependencies"
|
|
385
|
-
f" but container got {len(dep_object_ids)} object ids.\n"
|
|
386
|
-
f"Code deps: {service.service_deps}\n"
|
|
387
|
-
f"Object ids: {dep_object_ids}\n"
|
|
388
|
-
"\n"
|
|
389
|
-
"This can happen if you are defining Modal objects under a conditional statement "
|
|
390
|
-
"that evaluates differently in the local and remote environments."
|
|
391
|
-
)
|
|
392
|
-
for object_id, obj in zip(dep_object_ids, service.service_deps):
|
|
393
|
-
metadata: Message | None = container_app.object_handle_metadata[object_id]
|
|
394
|
-
obj._hydrate(object_id, _client, metadata)
|
|
444
|
+
service: Service = hydrate_function(
|
|
445
|
+
container_args, container_io_manager.get_task_lifecycle_manager(), function_def, _client, client
|
|
446
|
+
)
|
|
447
|
+
if function_def.pty_info.pty_type == api_pb2.PTYInfo.PTY_TYPE_SHELL:
|
|
448
|
+
# Concurrency and batching doesn't apply for `modal shell`.
|
|
449
|
+
batch_max_size = 0
|
|
450
|
+
batch_wait_ms = 0
|
|
451
|
+
else:
|
|
452
|
+
batch_max_size = function_def.batch_max_size or 0
|
|
453
|
+
batch_wait_ms = function_def.batch_linger_ms or 0
|
|
395
454
|
|
|
396
455
|
# Initialize clustered functions.
|
|
397
456
|
if function_def._experimental_group_size > 0:
|
|
@@ -407,10 +466,14 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
407
466
|
) as finalized_functions:
|
|
408
467
|
# This context managers handles pre/post snapshot lifecycle of the user code,
|
|
409
468
|
# finalized functions, ASGI/WSGI lifespan, and volume commit.
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
469
|
+
call_function(event_loop, container_io_manager, finalized_functions, batch_max_size, batch_wait_ms)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
473
|
+
if container_args.function_def.is_server:
|
|
474
|
+
run_server(container_args, client)
|
|
475
|
+
else:
|
|
476
|
+
run_function(container_args, client)
|
|
414
477
|
|
|
415
478
|
|
|
416
479
|
if __name__ == "__main__":
|
|
@@ -19,6 +19,7 @@ from modal_proto import api_pb2
|
|
|
19
19
|
from modal_proto.modal_api_grpc import ModalClientModal
|
|
20
20
|
|
|
21
21
|
from ._function_variants import _FunctionOptions, _make_function_variant
|
|
22
|
+
from ._image import _Image
|
|
22
23
|
from ._load_context import LoadContext
|
|
23
24
|
from ._object import _Object, live_method, live_method_gen
|
|
24
25
|
from ._output.pty import get_pty_info
|
|
@@ -65,7 +66,6 @@ from .exception import (
|
|
|
65
66
|
NotFoundError,
|
|
66
67
|
OutputExpiredError,
|
|
67
68
|
)
|
|
68
|
-
from .image import _Image
|
|
69
69
|
from .mount import _get_client_mount, _Mount
|
|
70
70
|
from .network_file_system import _NetworkFileSystem, network_file_system_mount_protos
|
|
71
71
|
from .output import OutputManager
|
|
@@ -8,7 +8,7 @@ import sys
|
|
|
8
8
|
import typing
|
|
9
9
|
import warnings
|
|
10
10
|
from collections.abc import Callable, Collection, Sequence
|
|
11
|
-
from dataclasses import dataclass
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
12
|
from inspect import isfunction
|
|
13
13
|
from pathlib import Path, PurePosixPath
|
|
14
14
|
from typing import (
|
|
@@ -32,7 +32,7 @@ from ._load_context import LoadContext
|
|
|
32
32
|
from ._object import _Object, live_method_gen
|
|
33
33
|
from ._resolver import Resolver
|
|
34
34
|
from ._serialization import get_preferred_payload_format, serialize
|
|
35
|
-
from ._utils.async_utils import TaskContext, deprecate_aio_usage,
|
|
35
|
+
from ._utils.async_utils import TaskContext, deprecate_aio_usage, synchronizer
|
|
36
36
|
from ._utils.blob_utils import MAX_OBJECT_SIZE_BYTES
|
|
37
37
|
from ._utils.docker_utils import (
|
|
38
38
|
extract_copy_command_patterns,
|
|
@@ -362,9 +362,8 @@ class _ImageRegistryConfig:
|
|
|
362
362
|
|
|
363
363
|
@dataclass
|
|
364
364
|
class DockerfileSpec:
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
context_files: dict[str, str]
|
|
365
|
+
commands: list[str] = field(default_factory=list)
|
|
366
|
+
context_files: dict[str, str] = field(default_factory=dict)
|
|
368
367
|
|
|
369
368
|
|
|
370
369
|
async def _image_await_build_result(image_id: str, client: _Client) -> api_pb2.ImageJoinStreamingResponse:
|
|
@@ -538,7 +537,7 @@ class _Image(_Object, type_prefix="im"):
|
|
|
538
537
|
raise InvalidError("Cannot run a build function with multiple base images!")
|
|
539
538
|
|
|
540
539
|
def _deps() -> Sequence[_Object]:
|
|
541
|
-
deps = tuple(base_images.values()) + tuple(secrets)
|
|
540
|
+
deps: tuple[_Object, ...] = tuple(base_images.values()) + tuple(secrets)
|
|
542
541
|
if build_function:
|
|
543
542
|
deps += (build_function,)
|
|
544
543
|
if image_registry_config and image_registry_config.secret:
|
|
@@ -670,10 +669,10 @@ class _Image(_Object, type_prefix="im"):
|
|
|
670
669
|
else:
|
|
671
670
|
# not built or in the process of building - wait for build
|
|
672
671
|
logger.debug("Waiting for image %s" % image_id)
|
|
673
|
-
|
|
674
|
-
result =
|
|
675
|
-
if
|
|
676
|
-
metadata =
|
|
672
|
+
build_resp = await _image_await_build_result(image_id, load_context.client)
|
|
673
|
+
result = build_resp.result
|
|
674
|
+
if build_resp.HasField("metadata"):
|
|
675
|
+
metadata = build_resp.metadata
|
|
677
676
|
|
|
678
677
|
if result.status == api_pb2.GenericResult.GENERIC_STATUS_FAILURE:
|
|
679
678
|
if result.exception:
|
|
@@ -701,7 +700,7 @@ class _Image(_Object, type_prefix="im"):
|
|
|
701
700
|
raise RemoteError("Unknown status %s!" % result.status)
|
|
702
701
|
|
|
703
702
|
self._hydrate(image_id, load_context.client, metadata)
|
|
704
|
-
local_mounts = set()
|
|
703
|
+
local_mounts: set[_Mount] = set()
|
|
705
704
|
for base in base_images.values():
|
|
706
705
|
local_mounts |= base._serve_mounts
|
|
707
706
|
if context_mount and context_mount.is_local():
|
|
@@ -1865,8 +1864,8 @@ class _Image(_Object, type_prefix="im"):
|
|
|
1865
1864
|
"""
|
|
1866
1865
|
if not isinstance(entrypoint_commands, list) or not all(isinstance(x, str) for x in entrypoint_commands):
|
|
1867
1866
|
raise InvalidError("entrypoint_commands must be a list of strings.")
|
|
1868
|
-
|
|
1869
|
-
args_str = '"' + '", "'.join(
|
|
1867
|
+
args = _flatten_str_args("entrypoint", "entrypoint_commands", entrypoint_commands)
|
|
1868
|
+
args_str = '"' + '", "'.join(args) + '"' if args else ""
|
|
1870
1869
|
dockerfile_cmd = f"ENTRYPOINT [{args_str}]"
|
|
1871
1870
|
|
|
1872
1871
|
return self.dockerfile_commands(dockerfile_cmd)
|
|
@@ -1885,8 +1884,8 @@ class _Image(_Object, type_prefix="im"):
|
|
|
1885
1884
|
"""
|
|
1886
1885
|
if not isinstance(shell_commands, list) or not all(isinstance(x, str) for x in shell_commands):
|
|
1887
1886
|
raise InvalidError("shell_commands must be a list of strings.")
|
|
1888
|
-
|
|
1889
|
-
args_str = '"' + '", "'.join(
|
|
1887
|
+
args = _flatten_str_args("shell", "shell_commands", shell_commands)
|
|
1888
|
+
args_str = '"' + '", "'.join(args) + '"' if args else ""
|
|
1890
1889
|
dockerfile_cmd = f"SHELL [{args_str}]"
|
|
1891
1890
|
|
|
1892
1891
|
return self.dockerfile_commands(dockerfile_cmd)
|
|
@@ -2639,7 +2638,7 @@ class _Image(_Object, type_prefix="im"):
|
|
|
2639
2638
|
function = _Function.from_local(
|
|
2640
2639
|
info,
|
|
2641
2640
|
app=None,
|
|
2642
|
-
image=self,
|
|
2641
|
+
image=self,
|
|
2643
2642
|
secrets=secrets,
|
|
2644
2643
|
gpu=gpu,
|
|
2645
2644
|
volumes=volumes,
|
|
@@ -2756,8 +2755,8 @@ class _Image(_Object, type_prefix="im"):
|
|
|
2756
2755
|
if not isinstance(cmd, list) or not all(isinstance(x, str) for x in cmd):
|
|
2757
2756
|
raise InvalidError("Image CMD must be a list of strings.")
|
|
2758
2757
|
|
|
2759
|
-
|
|
2760
|
-
cmd_str = '"' + '", "'.join(
|
|
2758
|
+
cmd_args = _flatten_str_args("cmd", "cmd", cmd)
|
|
2759
|
+
cmd_str = '"' + '", "'.join(cmd_args) + '"' if cmd_args else ""
|
|
2761
2760
|
dockerfile_cmd = f"CMD [{cmd_str}]"
|
|
2762
2761
|
|
|
2763
2762
|
return self.dockerfile_commands(dockerfile_cmd)
|
|
@@ -2849,6 +2848,3 @@ class _Image(_Object, type_prefix="im"):
|
|
|
2849
2848
|
"Images cannot currently be hydrated on demand; you can build an Image by running an App that uses it."
|
|
2850
2849
|
)
|
|
2851
2850
|
return self
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
Image = synchronize_api(_Image)
|
|
@@ -7,6 +7,7 @@ from modal._utils.async_utils import synchronize_api
|
|
|
7
7
|
from modal.exception import InvalidError
|
|
8
8
|
|
|
9
9
|
from .container_io_manager import _ContainerIOManager
|
|
10
|
+
from .task_lifecycle_manager import _TaskLifecycleManager
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def is_local() -> bool:
|
|
@@ -18,7 +19,7 @@ def is_local() -> bool:
|
|
|
18
19
|
even though those processes are running on Modal hardware.
|
|
19
20
|
|
|
20
21
|
"""
|
|
21
|
-
return
|
|
22
|
+
return _TaskLifecycleManager._singleton is None
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
async def _interact() -> None:
|
|
@@ -29,7 +30,7 @@ async def _interact() -> None:
|
|
|
29
30
|
"""
|
|
30
31
|
container_io_manager = _ContainerIOManager._singleton
|
|
31
32
|
if not container_io_manager:
|
|
32
|
-
raise InvalidError("Interactivity only works inside a Modal container.")
|
|
33
|
+
raise InvalidError("Interactivity only works inside a Modal Function's container.")
|
|
33
34
|
else:
|
|
34
35
|
await container_io_manager.interact()
|
|
35
36
|
|
|
@@ -7,6 +7,10 @@ import sys
|
|
|
7
7
|
import traceback
|
|
8
8
|
from collections.abc import AsyncGenerator
|
|
9
9
|
from pathlib import Path
|
|
10
|
+
from typing import (
|
|
11
|
+
ClassVar,
|
|
12
|
+
Optional,
|
|
13
|
+
)
|
|
10
14
|
|
|
11
15
|
from synchronicity.async_wrap import asynccontextmanager
|
|
12
16
|
|
|
@@ -30,6 +34,8 @@ class UserException(Exception):
|
|
|
30
34
|
|
|
31
35
|
|
|
32
36
|
class _TaskLifecycleManager:
|
|
37
|
+
_singleton: ClassVar[Optional["_TaskLifecycleManager"]] = None
|
|
38
|
+
|
|
33
39
|
task_id: str
|
|
34
40
|
function_id: str
|
|
35
41
|
function_def: api_pb2.Function
|
|
@@ -37,7 +43,7 @@ class _TaskLifecycleManager:
|
|
|
37
43
|
_client: _Client
|
|
38
44
|
_cuda_checkpoint_session: gpu_memory_snapshot.CudaCheckpointSession | None
|
|
39
45
|
|
|
40
|
-
def
|
|
46
|
+
def _init(
|
|
41
47
|
self,
|
|
42
48
|
task_id: str,
|
|
43
49
|
function_id: str,
|
|
@@ -52,6 +58,23 @@ class _TaskLifecycleManager:
|
|
|
52
58
|
self._client = client
|
|
53
59
|
self._cuda_checkpoint_session = None
|
|
54
60
|
|
|
61
|
+
def __new__(
|
|
62
|
+
cls,
|
|
63
|
+
task_id: str,
|
|
64
|
+
function_id: str,
|
|
65
|
+
function_def: api_pb2.Function,
|
|
66
|
+
checkpoint_id: str | None,
|
|
67
|
+
client: _Client,
|
|
68
|
+
) -> "_TaskLifecycleManager":
|
|
69
|
+
cls._singleton = super().__new__(cls)
|
|
70
|
+
cls._singleton._init(task_id, function_id, function_def, checkpoint_id, client)
|
|
71
|
+
return cls._singleton
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def _reset_singleton(cls) -> None:
|
|
75
|
+
"""Only used for tests."""
|
|
76
|
+
cls._singleton = None
|
|
77
|
+
|
|
55
78
|
@asynccontextmanager
|
|
56
79
|
async def handle_task_lifecycle_exception(
|
|
57
80
|
self,
|
|
@@ -11,6 +11,7 @@ class UserException(Exception):
|
|
|
11
11
|
...
|
|
12
12
|
|
|
13
13
|
class _TaskLifecycleManager:
|
|
14
|
+
_singleton: typing.ClassVar[typing.Optional[_TaskLifecycleManager]]
|
|
14
15
|
task_id: str
|
|
15
16
|
function_id: str
|
|
16
17
|
function_def: modal_proto.api_pb2.Function
|
|
@@ -18,15 +19,29 @@ class _TaskLifecycleManager:
|
|
|
18
19
|
_client: modal.client._Client
|
|
19
20
|
_cuda_checkpoint_session: typing.Optional[modal._runtime.gpu_memory_snapshot.CudaCheckpointSession]
|
|
20
21
|
|
|
21
|
-
def
|
|
22
|
+
def _init(
|
|
22
23
|
self,
|
|
23
24
|
task_id: str,
|
|
24
25
|
function_id: str,
|
|
25
26
|
function_def: modal_proto.api_pb2.Function,
|
|
26
27
|
checkpoint_id: typing.Optional[str],
|
|
27
28
|
client: modal.client._Client,
|
|
28
|
-
) -> None:
|
|
29
|
-
|
|
29
|
+
) -> None: ...
|
|
30
|
+
@staticmethod
|
|
31
|
+
def __new__(
|
|
32
|
+
cls,
|
|
33
|
+
task_id: str,
|
|
34
|
+
function_id: str,
|
|
35
|
+
function_def: modal_proto.api_pb2.Function,
|
|
36
|
+
checkpoint_id: typing.Optional[str],
|
|
37
|
+
client: modal.client._Client,
|
|
38
|
+
) -> _TaskLifecycleManager:
|
|
39
|
+
"""Create and return a new object. See help(type) for accurate signature."""
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def _reset_singleton(cls) -> None:
|
|
44
|
+
"""Only used for tests."""
|
|
30
45
|
...
|
|
31
46
|
|
|
32
47
|
def handle_task_lifecycle_exception(self) -> typing.AsyncContextManager[None]:
|
|
@@ -45,6 +60,7 @@ class _TaskLifecycleManager:
|
|
|
45
60
|
...
|
|
46
61
|
|
|
47
62
|
class TaskLifecycleManager:
|
|
63
|
+
_singleton: typing.ClassVar[typing.Optional[TaskLifecycleManager]]
|
|
48
64
|
task_id: str
|
|
49
65
|
function_id: str
|
|
50
66
|
function_def: modal_proto.api_pb2.Function
|
|
@@ -52,7 +68,11 @@ class TaskLifecycleManager:
|
|
|
52
68
|
_client: modal.client.Client
|
|
53
69
|
_cuda_checkpoint_session: typing.Optional[modal._runtime.gpu_memory_snapshot.CudaCheckpointSession]
|
|
54
70
|
|
|
55
|
-
def __init__(
|
|
71
|
+
def __init__(self, /, *args, **kwargs):
|
|
72
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
def _init(
|
|
56
76
|
self,
|
|
57
77
|
task_id: str,
|
|
58
78
|
function_id: str,
|
|
@@ -60,6 +80,10 @@ class TaskLifecycleManager:
|
|
|
60
80
|
checkpoint_id: typing.Optional[str],
|
|
61
81
|
client: modal.client.Client,
|
|
62
82
|
) -> None: ...
|
|
83
|
+
@classmethod
|
|
84
|
+
def _reset_singleton(cls) -> None:
|
|
85
|
+
"""Only used for tests."""
|
|
86
|
+
...
|
|
63
87
|
|
|
64
88
|
class __handle_task_lifecycle_exception_spec(typing_extensions.Protocol):
|
|
65
89
|
def __call__(self, /) -> synchronicity.combined_types.AsyncAndBlockingContextManager[None]:
|
|
@@ -18,6 +18,7 @@ from synchronicity.async_wrap import asynccontextmanager
|
|
|
18
18
|
from modal_proto import api_pb2
|
|
19
19
|
|
|
20
20
|
from ._functions import _Function
|
|
21
|
+
from ._image import _Image
|
|
21
22
|
from ._ipython import is_interactive_ipython
|
|
22
23
|
from ._load_context import LoadContext
|
|
23
24
|
from ._object import _get_environment_name, _Object
|
|
@@ -47,7 +48,6 @@ from .cls import _Cls, parameter
|
|
|
47
48
|
from .config import logger
|
|
48
49
|
from .exception import ExecutionError, InvalidError
|
|
49
50
|
from .functions import Function
|
|
50
|
-
from .image import _Image
|
|
51
51
|
from .network_file_system import _NetworkFileSystem
|
|
52
52
|
from .partial_function import PartialFunction
|
|
53
53
|
from .proxy import _Proxy
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import collections.abc
|
|
2
2
|
import modal._functions
|
|
3
|
+
import modal._image
|
|
3
4
|
import modal._load_context
|
|
4
5
|
import modal._partial_function
|
|
5
6
|
import modal._server
|
|
@@ -80,7 +81,7 @@ class _LocalAppState:
|
|
|
80
81
|
|
|
81
82
|
functions: dict[str, modal._functions._Function]
|
|
82
83
|
classes: dict[str, modal.cls._Cls]
|
|
83
|
-
image_default: typing.Optional[modal.
|
|
84
|
+
image_default: typing.Optional[modal._image._Image]
|
|
84
85
|
web_endpoints: list[str]
|
|
85
86
|
local_entrypoints: dict[str, _LocalEntrypoint]
|
|
86
87
|
tags: dict[str, str]
|
|
@@ -92,7 +93,7 @@ class _LocalAppState:
|
|
|
92
93
|
self,
|
|
93
94
|
functions: dict[str, modal._functions._Function],
|
|
94
95
|
classes: dict[str, modal.cls._Cls],
|
|
95
|
-
image_default: typing.Optional[modal.
|
|
96
|
+
image_default: typing.Optional[modal._image._Image],
|
|
96
97
|
web_endpoints: list[str],
|
|
97
98
|
local_entrypoints: dict[str, _LocalEntrypoint],
|
|
98
99
|
tags: dict[str, str],
|
|
@@ -163,7 +164,7 @@ class _App:
|
|
|
163
164
|
name: typing.Optional[str] = None,
|
|
164
165
|
*,
|
|
165
166
|
tags: typing.Optional[dict[str, str]] = None,
|
|
166
|
-
image: typing.Optional[modal.
|
|
167
|
+
image: typing.Optional[modal._image._Image] = None,
|
|
167
168
|
secrets: collections.abc.Sequence[modal.secret._Secret] = [],
|
|
168
169
|
volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume] = {},
|
|
169
170
|
include_source: bool = True,
|
|
@@ -282,7 +283,7 @@ class _App:
|
|
|
282
283
|
|
|
283
284
|
def _validate_blueprint_value(self, key: str, value: typing.Any): ...
|
|
284
285
|
@property
|
|
285
|
-
def image(self) -> modal.
|
|
286
|
+
def image(self) -> modal._image._Image:
|
|
286
287
|
"""mdmd:hidden
|
|
287
288
|
Retrieve the Image that will be used as the default for any Functions registered to the App.
|
|
288
289
|
|
|
@@ -546,7 +547,7 @@ class _App:
|
|
|
546
547
|
self,
|
|
547
548
|
_warn_parentheses_missing=None,
|
|
548
549
|
*,
|
|
549
|
-
image: typing.Optional[modal.
|
|
550
|
+
image: typing.Optional[modal._image._Image] = None,
|
|
550
551
|
schedule: typing.Optional[modal.schedule.Schedule] = None,
|
|
551
552
|
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
552
553
|
secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
|
|
@@ -645,7 +646,7 @@ class _App:
|
|
|
645
646
|
self,
|
|
646
647
|
_warn_parentheses_missing=None,
|
|
647
648
|
*,
|
|
648
|
-
image: typing.Optional[modal.
|
|
649
|
+
image: typing.Optional[modal._image._Image] = None,
|
|
649
650
|
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
650
651
|
secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
|
|
651
652
|
gpu: typing.Union[str, list[str], None] = None,
|
|
@@ -731,7 +732,7 @@ class _App:
|
|
|
731
732
|
self,
|
|
732
733
|
_warn_parentheses_missing=None,
|
|
733
734
|
*,
|
|
734
|
-
image: typing.Optional[modal.
|
|
735
|
+
image: typing.Optional[modal._image._Image] = None,
|
|
735
736
|
env: typing.Optional[dict[str, typing.Optional[str]]] = None,
|
|
736
737
|
secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
|
|
737
738
|
gpu: typing.Union[str, list[str], None] = None,
|
|
@@ -1948,4 +1949,4 @@ class App:
|
|
|
1948
1949
|
"""Only used for tests."""
|
|
1949
1950
|
...
|
|
1950
1951
|
|
|
1951
|
-
_default_image: modal.
|
|
1952
|
+
_default_image: modal._image._Image
|