modal 1.5.3.dev12__tar.gz → 1.5.4.dev0__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.5.3.dev12 → modal-1.5.4.dev0}/PKG-INFO +1 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_clustered_functions.py +11 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_clustered_functions.pyi +6 -2
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_environments.py +67 -37
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_grpc_client.py +1 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_object.py +5 -4
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_output/rich.py +3 -3
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_resources.py +4 -4
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/user_code_imports.py +2 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_traceback.py +1 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/async_utils.py +5 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/blob_utils.py +1 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/bytes_io_segment_payload.py +2 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/pattern_utils.py +2 -2
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/time_utils.py +3 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/app.py +3 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/app.pyi +4 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/cluster.py +1 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/dashboard.py +1 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/endpoint.py +2 -2
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/environment.py +104 -42
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/launch.py +1 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/network_file_system.py +1 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/profile.py +3 -3
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/shell.py +2 -2
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/client.pyi +2 -2
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cloud_bucket_mount.py +3 -2
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/environments.py +3 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/environments.pyi +68 -53
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/experimental/__init__.py +11 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/file_pattern_matcher.py +1 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/functions.pyi +6 -6
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/mount.py +89 -31
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/mount.pyi +18 -6
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/secret.py +2 -2
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/serving.py +3 -3
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/api_pb2.py +274 -274
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/api_pb2.pyi +6 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2.py +32 -32
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2.pyi +5 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_version/__init__.py +1 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/pyproject.toml +48 -1
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/LICENSE +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/README.md +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/__init__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/__main__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_billing.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_container_entrypoint.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_function_variants.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_functions.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_image.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_ipython.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_load_context.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_location.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_logs.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_logs_manager.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_logs_manager.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_output/__init__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_output/manager.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_output/pty.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_output/status.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_partial_function.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_resolver.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/__init__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/asgi.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/execution_context.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/telemetry.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_serialization.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_server.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_supports_logs.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_tunnel.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_tunnel.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_type_manager.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/__init__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/app_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/browser_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/curl_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/deprecation.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/display_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/docker_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/function_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/git_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/hash_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/http_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/logger.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/mount_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/name_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/package_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/proxy_support.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/shell_utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_vendor/__init__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_vendor/tblib.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_vendor/version.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_watcher.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/_workspace.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/billing.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/billing.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/builder/2023.12.txt +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/builder/2024.04.txt +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/builder/2024.10.txt +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/builder/2025.06.txt +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/builder/README.md +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/builder/base-images.json +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/call_graph.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/__init__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/_download.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/_help.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/_traceback.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/app.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/billing.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/bootstrap.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/changelog.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/config.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/container.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/curl.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/dict.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/entry_point.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/image.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/import_refs.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/logo.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/programs/__init__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/programs/vscode.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/queues.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/run.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/secret.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/selector.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/skills.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/token.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/utils.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/volume.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cli/workspace.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/client.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cls.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/cls.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/config.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/container_process.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/container_process.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/dict.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/dict.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/exception.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/experimental/flash.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/experimental/flash.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/experimental/ipython.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/file_io.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/file_io.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/functions.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/image.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/image.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/io_streams.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/io_streams.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/network_file_system.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/network_file_system.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/object.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/object.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/output.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/parallel_map.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/parallel_map.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/partial_function.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/partial_function.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/proxy.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/proxy.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/py.typed +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/queue.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/queue.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/retries.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/runner.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/runner.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/running_app.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/sandbox.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/sandbox.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/sandbox_fs.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/sandbox_fs.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/schedule.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/scheduler_placement.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/secret.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/server.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/server.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/serving.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/snapshot.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/snapshot.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/stream_type.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/token_flow.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/token_flow.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/types.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/volume.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/volume.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/workspace.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal/workspace.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal.egg-info/requires.txt +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal.egg-info/top_level.txt +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_docs/__init__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_docs/mdmd/types.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_docs/reference_intro.md +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/__init__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/api_grpc.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/py.typed +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/modal_version/__main__.py +0 -0
- {modal-1.5.3.dev12 → modal-1.5.4.dev0}/setup.cfg +0 -0
|
@@ -15,6 +15,7 @@ class ClusterInfo:
|
|
|
15
15
|
cluster_id: str
|
|
16
16
|
container_ips: list[str]
|
|
17
17
|
container_ipv4_ips: list[str]
|
|
18
|
+
fabric_ids: list[str]
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
cluster_info: ClusterInfo | None = None
|
|
@@ -29,6 +30,14 @@ def get_cluster_info() -> ClusterInfo:
|
|
|
29
30
|
return cluster_info
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
def get_fabric_peers() -> list[int]:
|
|
34
|
+
info = get_cluster_info()
|
|
35
|
+
if info.rank >= len(info.fabric_ids) or not info.fabric_ids[info.rank]:
|
|
36
|
+
return [info.rank]
|
|
37
|
+
own_fabric_id = info.fabric_ids[info.rank]
|
|
38
|
+
return [rank for rank, fabric_id in enumerate(info.fabric_ids) if fabric_id == own_fabric_id]
|
|
39
|
+
|
|
40
|
+
|
|
32
41
|
async def _initialize_clustered_function(client: _Client, task_id: str, world_size: int):
|
|
33
42
|
global cluster_info
|
|
34
43
|
|
|
@@ -70,6 +79,7 @@ async def _initialize_clustered_function(client: _Client, task_id: str, world_si
|
|
|
70
79
|
cluster_id=resp.cluster_id,
|
|
71
80
|
container_ips=resp.container_ips,
|
|
72
81
|
container_ipv4_ips=resp.container_ipv4_ips,
|
|
82
|
+
fabric_ids=resp.fabric_ids,
|
|
73
83
|
)
|
|
74
84
|
else:
|
|
75
85
|
cluster_info = ClusterInfo(
|
|
@@ -77,6 +87,7 @@ async def _initialize_clustered_function(client: _Client, task_id: str, world_si
|
|
|
77
87
|
cluster_id="", # No cluster ID for single-node # TODO(irfansharif): Is this right?
|
|
78
88
|
container_ips=[container_ip],
|
|
79
89
|
container_ipv4_ips=[], # No IPv4 IPs for single-node
|
|
90
|
+
fabric_ids=[],
|
|
80
91
|
)
|
|
81
92
|
|
|
82
93
|
|
|
@@ -3,14 +3,17 @@ import types
|
|
|
3
3
|
import typing_extensions
|
|
4
4
|
|
|
5
5
|
class ClusterInfo:
|
|
6
|
-
"""ClusterInfo(rank: int, cluster_id: str, container_ips: list[str], container_ipv4_ips: list[str])"""
|
|
6
|
+
"""ClusterInfo(rank: int, cluster_id: str, container_ips: list[str], container_ipv4_ips: list[str], fabric_ids: list[str])"""
|
|
7
7
|
|
|
8
8
|
rank: int
|
|
9
9
|
cluster_id: str
|
|
10
10
|
container_ips: list[str]
|
|
11
11
|
container_ipv4_ips: list[str]
|
|
12
|
+
fabric_ids: list[str]
|
|
12
13
|
|
|
13
|
-
def __init__(
|
|
14
|
+
def __init__(
|
|
15
|
+
self, rank: int, cluster_id: str, container_ips: list[str], container_ipv4_ips: list[str], fabric_ids: list[str]
|
|
16
|
+
) -> None:
|
|
14
17
|
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
15
18
|
...
|
|
16
19
|
|
|
@@ -23,6 +26,7 @@ class ClusterInfo:
|
|
|
23
26
|
...
|
|
24
27
|
|
|
25
28
|
def get_cluster_info() -> ClusterInfo: ...
|
|
29
|
+
def get_fabric_peers() -> list[int]: ...
|
|
26
30
|
async def _initialize_clustered_function(client: modal.client._Client, task_id: str, world_size: int): ...
|
|
27
31
|
|
|
28
32
|
class __initialize_clustered_function_spec(typing_extensions.Protocol):
|
|
@@ -16,6 +16,7 @@ from modal_proto import api_pb2
|
|
|
16
16
|
from ._load_context import LoadContext
|
|
17
17
|
from ._object import _Object
|
|
18
18
|
from ._resolver import Resolver
|
|
19
|
+
from ._utils.deprecation import deprecation_warning
|
|
19
20
|
from ._utils.name_utils import check_environment_name
|
|
20
21
|
from ._utils.time_utils import is_utc_month_aligned, parse_billing_cycle
|
|
21
22
|
from .client import _Client
|
|
@@ -104,7 +105,7 @@ class _EnvironmentManager:
|
|
|
104
105
|
await client.stub.EnvironmentDelete(api_pb2.EnvironmentDeleteRequest(name=name))
|
|
105
106
|
|
|
106
107
|
|
|
107
|
-
class
|
|
108
|
+
class _EnvironmentRolesManager:
|
|
108
109
|
"""mdmd:namespace"""
|
|
109
110
|
|
|
110
111
|
def __init__(self, environment: "_Environment"):
|
|
@@ -112,13 +113,13 @@ class _EnvironmentMembersManager:
|
|
|
112
113
|
self._environment = environment
|
|
113
114
|
|
|
114
115
|
async def list(self) -> dict[Literal["users", "service_users"], dict[str, str]]:
|
|
115
|
-
"""Enumerate the
|
|
116
|
+
"""Enumerate the Environment Role for each user and service user in the workspace.
|
|
116
117
|
|
|
117
118
|
**Examples:**
|
|
118
119
|
|
|
119
120
|
```python notest
|
|
120
|
-
|
|
121
|
-
print(
|
|
121
|
+
roles = modal.Environment.from_name("my-env").roles.list()
|
|
122
|
+
print(roles)
|
|
122
123
|
# {
|
|
123
124
|
# "users": {"alice": "contributor", "bob": "viewer", "carol": "contributor"},
|
|
124
125
|
# "service_users": {"alice-bot": "contributor", "ops-bot": "viewer", "ci-bot": "no-access"},
|
|
@@ -145,16 +146,17 @@ class _EnvironmentMembersManager:
|
|
|
145
146
|
users: Mapping[str, str] | None = None,
|
|
146
147
|
service_users: Mapping[str, str] | None = None,
|
|
147
148
|
) -> None:
|
|
148
|
-
"""
|
|
149
|
+
"""Update the Environment Role of users and service users.
|
|
149
150
|
|
|
150
|
-
Each
|
|
151
|
-
|
|
151
|
+
Each role is one of 'contributor', 'viewer', or 'no-access'. Service users can be
|
|
152
|
+
assigned a role on any Environment, while workspace members can only be assigned a
|
|
153
|
+
role on restricted Environments.
|
|
152
154
|
|
|
153
155
|
**Examples:**
|
|
154
156
|
|
|
155
157
|
```python notest
|
|
156
158
|
env = modal.Environment.from_name("my-restricted-env")
|
|
157
|
-
env.
|
|
159
|
+
env.roles.update(
|
|
158
160
|
users={"alice": "contributor", "bob": "viewer"},
|
|
159
161
|
service_users={"alice-bot": "contributor"},
|
|
160
162
|
)
|
|
@@ -196,24 +198,59 @@ class _EnvironmentMembersManager:
|
|
|
196
198
|
|
|
197
199
|
await self._dispatch_role_updates(requests)
|
|
198
200
|
|
|
201
|
+
async def _dispatch_role_updates(self, requests: dict[str, api_pb2.EnvironmentRoleSetRequest]) -> None:
|
|
202
|
+
"""Send batched EnvironmentRoleSet RPCs and report all errors encountered."""
|
|
203
|
+
results = await asyncio.gather(
|
|
204
|
+
*(self._environment.client.stub.EnvironmentRoleSet(req) for req in requests.values()),
|
|
205
|
+
return_exceptions=True,
|
|
206
|
+
)
|
|
207
|
+
errors = [(label, result) for label, result in zip(requests.keys(), results) if isinstance(result, Exception)]
|
|
208
|
+
if errors:
|
|
209
|
+
n = len(errors)
|
|
210
|
+
header = f"{n} error{'s' if n != 1 else ''} occurred while updating Environment roles:"
|
|
211
|
+
details = "\n".join(f" - {label}: {e}" for label, e in errors)
|
|
212
|
+
raise WorkspaceManagementError(f"{header}\n{details}")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# Deprecated alias for `_EnvironmentRolesManager`; each method warns and delegates.
|
|
216
|
+
class _EnvironmentMembersManager:
|
|
217
|
+
"""mdmd:hidden"""
|
|
218
|
+
|
|
219
|
+
def __init__(self, environment: "_Environment"):
|
|
220
|
+
"""mdmd:hidden"""
|
|
221
|
+
self._environment = environment
|
|
222
|
+
|
|
223
|
+
async def list(self) -> dict[Literal["users", "service_users"], dict[str, str]]:
|
|
224
|
+
deprecation_warning(
|
|
225
|
+
(2026, 7, 23),
|
|
226
|
+
"`Environment.members.list()` is deprecated; use `Environment.roles.list()` instead.",
|
|
227
|
+
)
|
|
228
|
+
return await _EnvironmentRolesManager(self._environment).list()
|
|
229
|
+
|
|
230
|
+
async def update(
|
|
231
|
+
self,
|
|
232
|
+
*,
|
|
233
|
+
users: Mapping[str, str] | None = None,
|
|
234
|
+
service_users: Mapping[str, str] | None = None,
|
|
235
|
+
) -> None:
|
|
236
|
+
deprecation_warning(
|
|
237
|
+
(2026, 7, 23),
|
|
238
|
+
"`Environment.members.update()` is deprecated; use `Environment.roles.update()` instead.",
|
|
239
|
+
)
|
|
240
|
+
await _EnvironmentRolesManager(self._environment).update(users=users, service_users=service_users)
|
|
241
|
+
|
|
199
242
|
async def remove(
|
|
200
243
|
self,
|
|
201
244
|
*,
|
|
202
245
|
users: Iterable[str] | None = None,
|
|
203
246
|
service_users: Iterable[str] | None = None,
|
|
204
247
|
) -> None:
|
|
205
|
-
"""Remove
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
env = modal.Environment.from_name("my-restricted-env")
|
|
211
|
-
env.members.remove(
|
|
212
|
-
users=["alice"],
|
|
213
|
-
service_users=["alice-bot"],
|
|
248
|
+
"""Remove the Environment Role of users and service users, reverting them to the default."""
|
|
249
|
+
deprecation_warning(
|
|
250
|
+
(2026, 7, 23),
|
|
251
|
+
"`Environment.members.remove()` is deprecated. Environment Roles are now explicit; "
|
|
252
|
+
"set a role (e.g. 'no-access') with `Environment.roles.update()` instead.",
|
|
214
253
|
)
|
|
215
|
-
```
|
|
216
|
-
"""
|
|
217
254
|
await self._environment.hydrate()
|
|
218
255
|
users = users or []
|
|
219
256
|
service_users = service_users or []
|
|
@@ -247,20 +284,7 @@ class _EnvironmentMembersManager:
|
|
|
247
284
|
role=api_pb2.ENVIRONMENT_ROLE_UNSPECIFIED,
|
|
248
285
|
)
|
|
249
286
|
|
|
250
|
-
await self._dispatch_role_updates(requests)
|
|
251
|
-
|
|
252
|
-
async def _dispatch_role_updates(self, requests: dict[str, api_pb2.EnvironmentRoleSetRequest]) -> None:
|
|
253
|
-
"""Send batched EnvironmentRoleSet RPCs and report all errors encountered."""
|
|
254
|
-
results = await asyncio.gather(
|
|
255
|
-
*(self._environment.client.stub.EnvironmentRoleSet(req) for req in requests.values()),
|
|
256
|
-
return_exceptions=True,
|
|
257
|
-
)
|
|
258
|
-
errors = [(label, result) for label, result in zip(requests.keys(), results) if isinstance(result, Exception)]
|
|
259
|
-
if errors:
|
|
260
|
-
n = len(errors)
|
|
261
|
-
header = f"{n} error{'s' if n != 1 else ''} occurred while updating Environment members:"
|
|
262
|
-
details = "\n".join(f" - {label}: {e}" for label, e in errors)
|
|
263
|
-
raise WorkspaceManagementError(f"{header}\n{details}")
|
|
287
|
+
await _EnvironmentRolesManager(self._environment)._dispatch_role_updates(requests)
|
|
264
288
|
|
|
265
289
|
|
|
266
290
|
class _Environment(_Object, type_prefix="en"):
|
|
@@ -284,11 +308,17 @@ class _Environment(_Object, type_prefix="en"):
|
|
|
284
308
|
return _EnvironmentManager()
|
|
285
309
|
|
|
286
310
|
@property
|
|
287
|
-
def
|
|
288
|
-
"""Namespace with methods for managing the
|
|
311
|
+
def roles(self) -> "_EnvironmentRolesManager":
|
|
312
|
+
"""Namespace with methods for managing the Environment Roles of users and service users.
|
|
289
313
|
|
|
290
|
-
See https://modal.com/docs/guide/rbac for more information on
|
|
314
|
+
See https://modal.com/docs/guide/rbac for more information on Environment Roles.
|
|
291
315
|
"""
|
|
316
|
+
return _EnvironmentRolesManager(self)
|
|
317
|
+
|
|
318
|
+
@property
|
|
319
|
+
def members(self) -> "_EnvironmentMembersManager":
|
|
320
|
+
"""mdmd:hidden"""
|
|
321
|
+
# Deprecated alias for `Environment.roles`.
|
|
292
322
|
return _EnvironmentMembersManager(self)
|
|
293
323
|
|
|
294
324
|
# TODO(michael) Keeping this private for now until we decide what else should be in it
|
|
@@ -591,4 +621,4 @@ def ensure_env(environment_name: str | None = None) -> str:
|
|
|
591
621
|
if environment_name is not None:
|
|
592
622
|
config.override_locally("environment", environment_name)
|
|
593
623
|
|
|
594
|
-
return config.get("environment")
|
|
624
|
+
return config.get("environment") or ""
|
|
@@ -54,7 +54,7 @@ class grpc_error_converter:
|
|
|
54
54
|
with suppress_tb_frame():
|
|
55
55
|
if isinstance(exc, GRPCError):
|
|
56
56
|
modal_exc = _STATUS_TO_EXCEPTION[exc.status](exc.message)
|
|
57
|
-
modal_exc._grpc_message = exc.message
|
|
57
|
+
modal_exc._grpc_message = exc.message or ""
|
|
58
58
|
modal_exc._grpc_status = exc.status
|
|
59
59
|
modal_exc._grpc_details = exc.details
|
|
60
60
|
if use_full_traceback:
|
|
@@ -23,19 +23,20 @@ EPHEMERAL_OBJECT_HEARTBEAT_SLEEP: int = 300
|
|
|
23
23
|
|
|
24
24
|
def _get_environment_name(
|
|
25
25
|
environment_name: str | None = None,
|
|
26
|
-
) -> str
|
|
26
|
+
) -> str:
|
|
27
27
|
"""Get environment name from various sources.
|
|
28
28
|
|
|
29
29
|
Args:
|
|
30
|
-
environment_name: Explicitly provided environment name (highest priority)
|
|
30
|
+
environment_name: Explicitly provided environment name (highest priority).
|
|
31
31
|
|
|
32
32
|
Returns:
|
|
33
|
-
Environment name from
|
|
33
|
+
Environment name from as passed in, from config, or as a null string
|
|
34
|
+
(which implies workspace-level default).
|
|
34
35
|
"""
|
|
35
36
|
if environment_name:
|
|
36
37
|
return environment_name
|
|
37
38
|
else:
|
|
38
|
-
return config.get("environment")
|
|
39
|
+
return config.get("environment") or ""
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
def live_method(method):
|
|
@@ -81,11 +81,11 @@ class FunctionQueuingColumn(ProgressColumn):
|
|
|
81
81
|
super().__init__()
|
|
82
82
|
|
|
83
83
|
def render(self, task) -> Text:
|
|
84
|
-
self.lag = max(task.completed - task.elapsed, self.lag)
|
|
84
|
+
self.lag = max(task.completed - (task.elapsed or 0), self.lag)
|
|
85
85
|
if task.finished:
|
|
86
|
-
elapsed = max(task.finished_time, task.completed)
|
|
86
|
+
elapsed = max(task.finished_time or 0, task.completed)
|
|
87
87
|
else:
|
|
88
|
-
elapsed = task.elapsed + self.lag
|
|
88
|
+
elapsed = (task.elapsed or 0) + self.lag
|
|
89
89
|
delta = timedelta(seconds=int(elapsed))
|
|
90
90
|
return Text(str(delta), style="progress.elapsed")
|
|
91
91
|
|
|
@@ -26,10 +26,10 @@ def convert_fn_config_to_resources_config(
|
|
|
26
26
|
raise InvalidError(f"Cannot specify a CPU limit lower than request: {milli_cpu_max} < {milli_cpu}")
|
|
27
27
|
elif cpu and isinstance(cpu, (float, int)):
|
|
28
28
|
milli_cpu = int(1000 * cpu)
|
|
29
|
-
milli_cpu_max =
|
|
29
|
+
milli_cpu_max = 0
|
|
30
30
|
else:
|
|
31
|
-
milli_cpu =
|
|
32
|
-
milli_cpu_max =
|
|
31
|
+
milli_cpu = 0
|
|
32
|
+
milli_cpu_max = 0
|
|
33
33
|
|
|
34
34
|
if memory and isinstance(memory, int):
|
|
35
35
|
memory_mb = memory
|
|
@@ -47,6 +47,6 @@ def convert_fn_config_to_resources_config(
|
|
|
47
47
|
gpu_config=parse_gpu_config(gpu),
|
|
48
48
|
memory_mb=memory_mb,
|
|
49
49
|
memory_mb_max=memory_mb_max,
|
|
50
|
-
ephemeral_disk_mb=ephemeral_disk,
|
|
50
|
+
ephemeral_disk_mb=ephemeral_disk or 0,
|
|
51
51
|
rdma=rdma or False,
|
|
52
52
|
)
|
|
@@ -549,7 +549,8 @@ def _get_cls_or_user_cls(
|
|
|
549
549
|
else:
|
|
550
550
|
# Load the module dynamically for non-serialized class.
|
|
551
551
|
module = importlib.import_module(function_def.module_name)
|
|
552
|
-
|
|
552
|
+
# Always use implementation_name to find the class, default to function_name if implementation_name not set.
|
|
553
|
+
qual_name: str = function_def.implementation_name or function_def.function_name
|
|
553
554
|
|
|
554
555
|
if not is_global_object(qual_name):
|
|
555
556
|
raise LocalFunctionError("Attempted to load a class defined in a function scope")
|
|
@@ -75,7 +75,7 @@ def append_modal_tb(exc: BaseException, tb_dict: TBDictType, line_cache: LineCac
|
|
|
75
75
|
setattr(exc, "__line_cache__", line_cache)
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
def reduce_traceback_to_user_code(tb: TracebackType | None, user_source: str) -> TracebackType:
|
|
78
|
+
def reduce_traceback_to_user_code(tb: TracebackType | None, user_source: str) -> TracebackType | None:
|
|
79
79
|
"""Return a traceback that does not contain modal entrypoint or synchronicity frames."""
|
|
80
80
|
|
|
81
81
|
# Step forward all the way through the traceback and drop any "Modal support" frames
|
|
@@ -1266,3 +1266,8 @@ def deprecate_aio_usage(deprecation_date: tuple[int, int, int], readable_sync_ca
|
|
|
1266
1266
|
return MethodWithAio(sync_implementation, _async_proxy, synchronizer, is_classmethod=is_classmethod)
|
|
1267
1267
|
|
|
1268
1268
|
return deco
|
|
1269
|
+
|
|
1270
|
+
|
|
1271
|
+
async def async_generator_from_sync(lst: Iterable[T]) -> AsyncGenerator[T, None]:
|
|
1272
|
+
for item in lst:
|
|
1273
|
+
yield item
|
|
@@ -444,6 +444,7 @@ class FileUploadSpec:
|
|
|
444
444
|
mode: int # file permission bits (last 12 bits of st_mode)
|
|
445
445
|
size: int
|
|
446
446
|
content: bytes | None = None # Set for very small files to avoid double-read
|
|
447
|
+
exists_in_store: bool | None = None # set after existence check
|
|
447
448
|
|
|
448
449
|
def read_content(self) -> bytes:
|
|
449
450
|
"""Read content from source."""
|
|
@@ -45,7 +45,8 @@ class BytesIOSegmentPayload(Payload):
|
|
|
45
45
|
self.segment_length = segment_length
|
|
46
46
|
# seek to start of file segment we are interested in, in order to make .size() evaluate correctly
|
|
47
47
|
self._value.seek(self.initial_seek_pos + segment_start)
|
|
48
|
-
|
|
48
|
+
base_size = super().size
|
|
49
|
+
assert base_size is not None and self.segment_length <= base_size
|
|
49
50
|
self.chunk_size = chunk_size
|
|
50
51
|
self.progress_report_cb = progress_report_cb or (lambda *_, **__: None)
|
|
51
52
|
self.reset_state()
|
|
@@ -143,12 +143,12 @@ class Pattern:
|
|
|
143
143
|
else:
|
|
144
144
|
return False
|
|
145
145
|
elif self.match_type == MatchType.REGEXP:
|
|
146
|
-
return self.regexp.match(path) is not None
|
|
146
|
+
return self.regexp is not None and self.regexp.match(path) is not None
|
|
147
147
|
else:
|
|
148
148
|
return False
|
|
149
149
|
|
|
150
150
|
|
|
151
|
-
def read_ignorefile(reader: TextIO) -> list[str]:
|
|
151
|
+
def read_ignorefile(reader: TextIO | None) -> list[str]:
|
|
152
152
|
"""Read an ignore file from a reader and return the list of file patterns to
|
|
153
153
|
ignore, applying the following rules:
|
|
154
154
|
|
|
@@ -294,7 +294,9 @@ def relative_timestamp(dt: datetime) -> str:
|
|
|
294
294
|
|
|
295
295
|
|
|
296
296
|
def locale_tz() -> tzinfo:
|
|
297
|
-
|
|
297
|
+
tz = datetime.now().astimezone().tzinfo
|
|
298
|
+
assert tz is not None
|
|
299
|
+
return tz
|
|
298
300
|
|
|
299
301
|
|
|
300
302
|
def as_timestamp(arg: datetime | str | None) -> float:
|
|
@@ -1295,6 +1295,7 @@ class _App:
|
|
|
1295
1295
|
scaleup_window: int | None = None, # Stabilization window (seconds) of sustained demand before scaling up
|
|
1296
1296
|
scaledown_window: int | None = None, # Max idle time before scaling down (seconds)
|
|
1297
1297
|
startup_timeout: int = 30, # Maximum container startup time in seconds
|
|
1298
|
+
name: str | None = None, # Name of the server
|
|
1298
1299
|
port: int = 8000, # Port the HTTP server listens on
|
|
1299
1300
|
unauthenticated: bool = False, # Whether the endpoint requires proxy authentication, required by default.
|
|
1300
1301
|
h2_enabled: bool = False, # Enable HTTP/2
|
|
@@ -1341,6 +1342,7 @@ class _App:
|
|
|
1341
1342
|
scaleup_window: Seconds of sustained demand required before scaling up new containers.
|
|
1342
1343
|
scaledown_window: Maximum duration (in seconds) idle containers wait before scaling down.
|
|
1343
1344
|
startup_timeout: Maximum container startup time in seconds.
|
|
1345
|
+
name: Sets the Modal name of the function within the app, defaults to class name.
|
|
1344
1346
|
port: Port the HTTP server listens on.
|
|
1345
1347
|
unauthenticated: Whether the endpoint requires proxy authentication; required by default.
|
|
1346
1348
|
h2_enabled: Enable HTTP/2.
|
|
@@ -1423,7 +1425,7 @@ class _App:
|
|
|
1423
1425
|
local_state = self._local_state
|
|
1424
1426
|
|
|
1425
1427
|
# Create the FunctionInfo for the server, note we treat FunctionInfo as a class for servers
|
|
1426
|
-
info = FunctionInfo(None, serialized=serialized, user_cls=user_cls, name_override=user_cls.__name__)
|
|
1428
|
+
info = FunctionInfo(None, serialized=serialized, user_cls=user_cls, name_override=name or user_cls.__name__)
|
|
1427
1429
|
# Create the service function
|
|
1428
1430
|
service_function = _Function.from_local(
|
|
1429
1431
|
info,
|
|
@@ -751,6 +751,7 @@ class _App:
|
|
|
751
751
|
scaleup_window: typing.Optional[int] = None,
|
|
752
752
|
scaledown_window: typing.Optional[int] = None,
|
|
753
753
|
startup_timeout: int = 30,
|
|
754
|
+
name: typing.Optional[str] = None,
|
|
754
755
|
port: int = 8000,
|
|
755
756
|
unauthenticated: bool = False,
|
|
756
757
|
h2_enabled: bool = False,
|
|
@@ -797,6 +798,7 @@ class _App:
|
|
|
797
798
|
scaleup_window: Seconds of sustained demand required before scaling up new containers.
|
|
798
799
|
scaledown_window: Maximum duration (in seconds) idle containers wait before scaling down.
|
|
799
800
|
startup_timeout: Maximum container startup time in seconds.
|
|
801
|
+
name: Sets the Modal name of the function within the app, defaults to class name.
|
|
800
802
|
port: Port the HTTP server listens on.
|
|
801
803
|
unauthenticated: Whether the endpoint requires proxy authentication; required by default.
|
|
802
804
|
h2_enabled: Enable HTTP/2.
|
|
@@ -1755,6 +1757,7 @@ class App:
|
|
|
1755
1757
|
scaleup_window: typing.Optional[int] = None,
|
|
1756
1758
|
scaledown_window: typing.Optional[int] = None,
|
|
1757
1759
|
startup_timeout: int = 30,
|
|
1760
|
+
name: typing.Optional[str] = None,
|
|
1758
1761
|
port: int = 8000,
|
|
1759
1762
|
unauthenticated: bool = False,
|
|
1760
1763
|
h2_enabled: bool = False,
|
|
@@ -1799,6 +1802,7 @@ class App:
|
|
|
1799
1802
|
scaleup_window: Seconds of sustained demand required before scaling up new containers.
|
|
1800
1803
|
scaledown_window: Maximum duration (in seconds) idle containers wait before scaling down.
|
|
1801
1804
|
startup_timeout: Maximum container startup time in seconds.
|
|
1805
|
+
name: Sets the Modal name of the function within the app, defaults to class name.
|
|
1802
1806
|
port: Port the HTTP server listens on.
|
|
1803
1807
|
unauthenticated: Whether the endpoint requires proxy authentication; required by default.
|
|
1804
1808
|
h2_enabled: Enable HTTP/2.
|
|
@@ -51,7 +51,7 @@ async def list_(env: str | None = None, json: bool = False):
|
|
|
51
51
|
[
|
|
52
52
|
c.cluster_id,
|
|
53
53
|
c.app_id,
|
|
54
|
-
timestamp_to_localized_str(c.started_at, json)
|
|
54
|
+
timestamp_to_localized_str(c.started_at, json) or "Pending",
|
|
55
55
|
str(len(c.task_ids)),
|
|
56
56
|
]
|
|
57
57
|
)
|
|
@@ -21,7 +21,7 @@ async def dashboard(
|
|
|
21
21
|
if object_id:
|
|
22
22
|
url = f"https://modal.com/id/{object_id}"
|
|
23
23
|
else:
|
|
24
|
-
env = config.get("environment")
|
|
24
|
+
env = config.get("environment") or ""
|
|
25
25
|
request = api_pb2.WorkspaceDashboardUrlRequest(environment_name=env)
|
|
26
26
|
client = await _Client.from_env()
|
|
27
27
|
response = await client.stub.WorkspaceDashboardUrlGet(request)
|
|
@@ -207,7 +207,7 @@ async def create(
|
|
|
207
207
|
raise click.UsageError("--custom-volume-path requires --custom-volume-name.")
|
|
208
208
|
|
|
209
209
|
env_name = ensure_env(env)
|
|
210
|
-
environment_name = _get_environment_name(env_name)
|
|
210
|
+
environment_name = _get_environment_name(env_name)
|
|
211
211
|
client = await _Client.from_env()
|
|
212
212
|
|
|
213
213
|
compute_region_spec = api_pb2.EndpointComputeRegionSpec()
|
|
@@ -271,7 +271,7 @@ async def create(
|
|
|
271
271
|
async def list_(*, json: bool = False, env: Optional[str] = None):
|
|
272
272
|
"""List Endpoints that are provisioning or running in an environment."""
|
|
273
273
|
env_name = ensure_env(env)
|
|
274
|
-
environment_name = _get_environment_name(env_name)
|
|
274
|
+
environment_name = _get_environment_name(env_name)
|
|
275
275
|
client = await _Client.from_env()
|
|
276
276
|
|
|
277
277
|
items: list[api_pb2.EndpointListItem] = []
|