modal 1.5.5.dev5__tar.gz → 1.5.5.dev6__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.5.dev5 → modal-1.5.5.dev6}/PKG-INFO +1 -1
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_environments.py +4 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/_help.py +143 -4
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/app.py +51 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/config.py +3 -2
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/entry_point.py +9 -4
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/environment.py +18 -2
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/profile.py +4 -4
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/run.py +28 -1
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/token.py +3 -11
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/client.py +8 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/client.pyi +2 -2
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/config.py +7 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/environments.pyi +2 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/experimental/__init__.py +2 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/api_grpc.py +16 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/api_pb2.py +905 -905
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/api_pb2.pyi +8 -2
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/api_pb2_grpc.py +33 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/api_pb2_grpc.pyi +10 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/modal_api_grpc.py +1 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_version/__init__.py +1 -1
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/LICENSE +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/README.md +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/__init__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/__main__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_billing.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_clustered_functions.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_clustered_functions.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_container_entrypoint.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_function_variants.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_functions.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_grpc_client.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_image.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_ipython.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_load_context.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_location.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_logs.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_logs_manager.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_logs_manager.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_object.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_output/__init__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_output/manager.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_output/pty.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_output/rich.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_output/status.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_partial_function.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_resolver.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_resources.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/__init__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/asgi.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/execution_context.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/telemetry.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_serialization.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_server.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_supports_logs.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_traceback.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_tunnel.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_tunnel.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_type_manager.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/__init__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/app_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/async_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/blob_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/browser_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/curl_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/deprecation.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/display_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/docker_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/function_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/git_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/hash_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/http_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/logger.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/mount_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/name_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/package_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/proxy_support.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/shell_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_utils/time_utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_vendor/__init__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_vendor/tblib.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_vendor/version.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_watcher.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/_workspace.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/app.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/app.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/billing.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/billing.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/builder/2023.12.txt +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/builder/2024.04.txt +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/builder/2024.10.txt +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/builder/2025.06.txt +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/builder/README.md +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/builder/base-images.json +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/call_graph.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/__init__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/_download.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/_traceback.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/billing.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/bootstrap.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/changelog.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/cluster.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/container.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/curl.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/dashboard.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/dict.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/endpoint.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/image.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/import_refs.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/launch.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/logo.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/network_file_system.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/programs/__init__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/programs/vscode.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/queues.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/secret.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/selector.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/shell.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/skills.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/utils.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/volume.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cli/workspace.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cls.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/cls.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/container_process.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/container_process.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/dict.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/dict.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/environments.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/exception.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/experimental/flash.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/experimental/flash.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/experimental/ipython.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/file_io.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/file_io.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/file_pattern_matcher.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/functions.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/functions.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/image.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/image.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/io_streams.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/io_streams.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/mount.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/mount.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/network_file_system.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/network_file_system.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/object.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/object.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/output.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/parallel_map.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/parallel_map.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/partial_function.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/partial_function.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/proxy.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/proxy.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/py.typed +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/queue.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/queue.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/retries.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/runner.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/runner.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/running_app.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/sandbox.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/sandbox.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/sandbox_fs.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/sandbox_fs.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/schedule.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/scheduler_placement.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/secret.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/secret.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/server.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/server.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/serving.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/serving.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/snapshot.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/snapshot.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/stream_type.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/token_flow.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/token_flow.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/types.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/volume.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/volume.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/workspace.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal/workspace.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal.egg-info/requires.txt +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal.egg-info/top_level.txt +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_docs/__init__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_docs/mdmd/types.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_docs/reference_intro.md +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/__init__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/py.typed +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/modal_version/__main__.py +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/pyproject.toml +0 -0
- {modal-1.5.5.dev5 → modal-1.5.5.dev6}/setup.cfg +0 -0
|
@@ -39,6 +39,7 @@ class _EnvironmentManager:
|
|
|
39
39
|
name: str, # Name to use for the new Environment
|
|
40
40
|
*,
|
|
41
41
|
restricted: bool = False, # If True, enable RBAC restrictions on the Environment
|
|
42
|
+
default_role: str | None = None, # Default member Role for the Restricted Environment
|
|
42
43
|
experimental_options: dict[str, Any] | None = None, # Experimental options for Environment creation
|
|
43
44
|
client: _Client | None = None, # Optional client with Modal credentials
|
|
44
45
|
) -> None:
|
|
@@ -51,8 +52,11 @@ class _EnvironmentManager:
|
|
|
51
52
|
```
|
|
52
53
|
"""
|
|
53
54
|
check_environment_name(name)
|
|
55
|
+
|
|
54
56
|
client = await _Client.from_env() if client is None else client
|
|
55
57
|
request = api_pb2.EnvironmentCreateRequest(name=name, is_managed=restricted)
|
|
58
|
+
if default_role:
|
|
59
|
+
request.default_member_role_str = default_role
|
|
56
60
|
if (experimental_options or {}).get("is_public", False):
|
|
57
61
|
request.environment_type = api_pb2.ENVIRONMENT_TYPE_PUBLIC
|
|
58
62
|
await client.stub.EnvironmentCreate(request)
|
|
@@ -7,6 +7,7 @@ import inspect
|
|
|
7
7
|
import os
|
|
8
8
|
import shutil
|
|
9
9
|
import sys
|
|
10
|
+
from collections.abc import Sequence
|
|
10
11
|
from typing import Any
|
|
11
12
|
|
|
12
13
|
import click
|
|
@@ -28,6 +29,83 @@ _MAX_HELP_WIDTH = 80
|
|
|
28
29
|
_HELP_PADDING = 1
|
|
29
30
|
|
|
30
31
|
|
|
32
|
+
class ModalGlobalOption(click.Option):
|
|
33
|
+
"""An option supported at every level of the Modal CLI."""
|
|
34
|
+
|
|
35
|
+
def __init__(self, *args: Any, environment_variable: str, **kwargs: Any) -> None:
|
|
36
|
+
kwargs.setdefault("expose_value", False)
|
|
37
|
+
super().__init__(*args, **kwargs)
|
|
38
|
+
self.environment_variable = environment_variable
|
|
39
|
+
|
|
40
|
+
def set_environment_value(self, value: object) -> None:
|
|
41
|
+
os.environ[self.environment_variable] = str(value)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ModalProfileOption(ModalGlobalOption):
|
|
45
|
+
"""Global option that updates Modal's active-profile cache."""
|
|
46
|
+
|
|
47
|
+
def set_environment_value(self, value: object) -> None:
|
|
48
|
+
from modal.config import _set_profile
|
|
49
|
+
|
|
50
|
+
# The profile is a global variable and currently set when modal is imported
|
|
51
|
+
# We could probably clean this up and simplify to just setting MODAL_PROFILE
|
|
52
|
+
_set_profile(str(value))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _root_global_options(ctx: click.Context) -> list[tuple[ModalGlobalOption, click.Context]]:
|
|
56
|
+
root_ctx = ctx.find_root()
|
|
57
|
+
return [
|
|
58
|
+
(param, root_ctx) for param in root_ctx.command.get_params(root_ctx) if isinstance(param, ModalGlobalOption)
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _global_option_token_length(ctx: click.Context, args: list[str], index: int) -> int:
|
|
63
|
+
option_name = args[index].split("=", 1)[0]
|
|
64
|
+
for option, _ in _root_global_options(ctx):
|
|
65
|
+
if option_name in (*option.opts, *option.secondary_opts):
|
|
66
|
+
return 1 if "=" in args[index] or option.is_flag else option.nargs + 1
|
|
67
|
+
return 0
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _consume_global_options(ctx: click.Context, args: list[str]) -> list[str]:
|
|
71
|
+
remaining_args: list[str] = []
|
|
72
|
+
value: str | bool
|
|
73
|
+
index = 0
|
|
74
|
+
while index < len(args):
|
|
75
|
+
arg = args[index]
|
|
76
|
+
option_name, separator, option_value = arg.partition("=")
|
|
77
|
+
global_option = next(
|
|
78
|
+
(
|
|
79
|
+
option
|
|
80
|
+
for option, _ in _root_global_options(ctx)
|
|
81
|
+
if option_name in (*option.opts, *option.secondary_opts)
|
|
82
|
+
),
|
|
83
|
+
None,
|
|
84
|
+
)
|
|
85
|
+
if global_option is None:
|
|
86
|
+
remaining_args.append(arg)
|
|
87
|
+
index += 1
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
if separator:
|
|
91
|
+
value = option_value
|
|
92
|
+
index += 1
|
|
93
|
+
elif global_option.is_flag:
|
|
94
|
+
value = global_option.flag_value
|
|
95
|
+
if option_name in global_option.secondary_opts and isinstance(value, bool):
|
|
96
|
+
value = not value
|
|
97
|
+
index += 1
|
|
98
|
+
else:
|
|
99
|
+
if index + global_option.nargs >= len(args):
|
|
100
|
+
raise click.UsageError(f"Option '{option_name}' requires an argument.", ctx)
|
|
101
|
+
value = args[index + 1]
|
|
102
|
+
index += global_option.nargs + 1
|
|
103
|
+
|
|
104
|
+
global_option.set_environment_value(value)
|
|
105
|
+
|
|
106
|
+
return remaining_args
|
|
107
|
+
|
|
108
|
+
|
|
31
109
|
def use_rich_style() -> bool:
|
|
32
110
|
"""Whether help output should be rendered in the rich style."""
|
|
33
111
|
env = os.environ.get("MODAL_RICH_CLI") # TODO move to config
|
|
@@ -82,11 +160,11 @@ def _option_label(param: click.Parameter, ctx: click.Context) -> Text:
|
|
|
82
160
|
|
|
83
161
|
def _build_options(cmd: click.Command, ctx: click.Context) -> RenderableType | None:
|
|
84
162
|
rows: list[tuple[Text, str]] = []
|
|
85
|
-
for param in cmd
|
|
86
|
-
rec = param.get_help_record(
|
|
163
|
+
for param, param_ctx in _options_with_global_options(cmd, ctx):
|
|
164
|
+
rec = param.get_help_record(param_ctx)
|
|
87
165
|
if rec is None: # skips arguments and hidden options
|
|
88
166
|
continue
|
|
89
|
-
rows.append((_option_label(param,
|
|
167
|
+
rows.append((_option_label(param, param_ctx), rec[1] or ""))
|
|
90
168
|
if not rows:
|
|
91
169
|
return None
|
|
92
170
|
|
|
@@ -98,6 +176,36 @@ def _build_options(cmd: click.Command, ctx: click.Context) -> RenderableType | N
|
|
|
98
176
|
return Group(Text("Options", style=_HEADING_STYLE), table)
|
|
99
177
|
|
|
100
178
|
|
|
179
|
+
def _global_options(ctx: click.Context) -> Sequence[tuple[click.Option, click.Context]]:
|
|
180
|
+
root_ctx = ctx.find_root()
|
|
181
|
+
if root_ctx is ctx:
|
|
182
|
+
return []
|
|
183
|
+
return _root_global_options(ctx)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _options_with_global_options(cmd: click.Command, ctx: click.Context) -> list[tuple[click.Parameter, click.Context]]:
|
|
187
|
+
params = [(param, ctx) for param in cmd.get_params(ctx)]
|
|
188
|
+
if global_options := _global_options(ctx):
|
|
189
|
+
for index, (param, _) in enumerate(params):
|
|
190
|
+
if "--help" in param.opts or "--help" in param.secondary_opts:
|
|
191
|
+
params[index:index] = global_options
|
|
192
|
+
break
|
|
193
|
+
else:
|
|
194
|
+
params.extend(global_options)
|
|
195
|
+
return params
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _format_options(cmd: click.Command, ctx: click.Context, formatter: click.HelpFormatter) -> None:
|
|
199
|
+
records = [
|
|
200
|
+
rec
|
|
201
|
+
for param, param_ctx in _options_with_global_options(cmd, ctx)
|
|
202
|
+
if (rec := param.get_help_record(param_ctx)) is not None
|
|
203
|
+
]
|
|
204
|
+
if records:
|
|
205
|
+
with formatter.section("Options"):
|
|
206
|
+
formatter.write_dl(records)
|
|
207
|
+
|
|
208
|
+
|
|
101
209
|
def _build_epilog(cmd: click.Command) -> RenderableType | None:
|
|
102
210
|
if not cmd.epilog:
|
|
103
211
|
return None
|
|
@@ -114,6 +222,10 @@ def group_commands_by_panel(group: click.Group) -> dict[str, list[tuple[str, cli
|
|
|
114
222
|
return panels
|
|
115
223
|
|
|
116
224
|
|
|
225
|
+
def _has_visible_commands(group: click.Group) -> bool:
|
|
226
|
+
return bool(group_commands_by_panel(group))
|
|
227
|
+
|
|
228
|
+
|
|
117
229
|
def _build_commands(group: click.Group, available_width: int) -> RenderableType | None:
|
|
118
230
|
panels = group_commands_by_panel(group)
|
|
119
231
|
if not panels:
|
|
@@ -189,6 +301,9 @@ class ModalCommand(click.Command):
|
|
|
189
301
|
super().__init__(*args, **kwargs)
|
|
190
302
|
self.panel = panel
|
|
191
303
|
|
|
304
|
+
def format_options(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
|
|
305
|
+
_format_options(self, ctx, formatter)
|
|
306
|
+
|
|
192
307
|
def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
|
|
193
308
|
if not use_rich_style():
|
|
194
309
|
return super().format_help(ctx, formatter)
|
|
@@ -210,6 +325,7 @@ class ModalGroup(click.Group):
|
|
|
210
325
|
|
|
211
326
|
command_class = ModalCommand
|
|
212
327
|
group_class = type # nested @group.group() reuses the enclosing class
|
|
328
|
+
defer_global_option_parsing = False
|
|
213
329
|
|
|
214
330
|
def __init__(self, *args: Any, panel: str | None = None, **kwargs: Any) -> None:
|
|
215
331
|
# Default to showing help when a group is invoked with no subcommand.
|
|
@@ -232,6 +348,29 @@ class ModalGroup(click.Group):
|
|
|
232
348
|
if hidden is not None:
|
|
233
349
|
cmd.hidden = hidden
|
|
234
350
|
|
|
351
|
+
def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
|
|
352
|
+
index = 0
|
|
353
|
+
while index < len(args):
|
|
354
|
+
arg = args[index]
|
|
355
|
+
if global_option_length := _global_option_token_length(ctx, args, index):
|
|
356
|
+
index += global_option_length
|
|
357
|
+
continue
|
|
358
|
+
command = self.commands.get(arg)
|
|
359
|
+
if command and getattr(command, "defer_global_option_parsing", False):
|
|
360
|
+
args[:] = _consume_global_options(ctx, args[:index]) + args[index:]
|
|
361
|
+
break
|
|
362
|
+
index += 1
|
|
363
|
+
else:
|
|
364
|
+
args[:] = _consume_global_options(ctx, args)
|
|
365
|
+
|
|
366
|
+
return super().parse_args(ctx, args)
|
|
367
|
+
|
|
368
|
+
def format_options(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
|
|
369
|
+
if _has_visible_commands(self):
|
|
370
|
+
self.format_commands(ctx, formatter)
|
|
371
|
+
else:
|
|
372
|
+
_format_options(self, ctx, formatter)
|
|
373
|
+
|
|
235
374
|
def format_commands(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
|
|
236
375
|
# Replaces click's single flat "Commands:" section with one section per
|
|
237
376
|
# panel so the simple-style help output still preserves grouping.
|
|
@@ -249,7 +388,7 @@ class ModalGroup(click.Group):
|
|
|
249
388
|
[
|
|
250
389
|
_build_usage(self, ctx),
|
|
251
390
|
_build_help_text(self),
|
|
252
|
-
_build_options(self, ctx),
|
|
391
|
+
None if _has_visible_commands(self) else _build_options(self, ctx),
|
|
253
392
|
_build_commands(self, _available_width(console)),
|
|
254
393
|
_build_epilog(self),
|
|
255
394
|
],
|
|
@@ -351,6 +351,57 @@ async def logs(
|
|
|
351
351
|
)
|
|
352
352
|
|
|
353
353
|
|
|
354
|
+
@app_cli.command("promote", no_args_is_help=True, hidden=True)
|
|
355
|
+
@click.argument("app_identifier")
|
|
356
|
+
@click.argument("version")
|
|
357
|
+
@env_option
|
|
358
|
+
@synchronizer.create_blocking
|
|
359
|
+
async def promote(
|
|
360
|
+
app_identifier: str,
|
|
361
|
+
version: str,
|
|
362
|
+
*,
|
|
363
|
+
env: str | None = None,
|
|
364
|
+
):
|
|
365
|
+
"""Deploy a staged version from an App's deployment history.
|
|
366
|
+
|
|
367
|
+
When a staged version gets promoted, the app is deployed with a new version that
|
|
368
|
+
refers back to the staged version.
|
|
369
|
+
|
|
370
|
+
Examples:
|
|
371
|
+
|
|
372
|
+
Promote an App to a specific version:
|
|
373
|
+
|
|
374
|
+
```
|
|
375
|
+
modal app promote my-app v5
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Promote an App using its App ID instead of its name:
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
modal app promote ap-abcdefghABCDEFGH123456 v5
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
"""
|
|
385
|
+
if m := re.fullmatch(r"v?([1-9]\d*)", version):
|
|
386
|
+
version_number = int(m.group(1))
|
|
387
|
+
else:
|
|
388
|
+
raise UsageError(f"Invalid version specifier: {version}. Expected a positive version number, e.g. 'v5' or '5'.")
|
|
389
|
+
|
|
390
|
+
env = ensure_env(env)
|
|
391
|
+
client = await _Client.from_env()
|
|
392
|
+
app_id, environment_name, lifecycle = await resolve_app_identifier(app_identifier, env, client)
|
|
393
|
+
if lifecycle.app_state != api_pb2.APP_STATE_DEPLOYED:
|
|
394
|
+
env_suffix = f" in the '{environment_name}' environment" if environment_name else ""
|
|
395
|
+
raise InvalidError(f"App '{app_identifier}' is not deployed{env_suffix}.")
|
|
396
|
+
|
|
397
|
+
resp = await client.stub.AppPromote(api_pb2.AppPromoteRequest(app_id=app_id, version=version_number))
|
|
398
|
+
print_server_warnings(resp.server_warnings)
|
|
399
|
+
|
|
400
|
+
output_mgr = OutputManager.get()
|
|
401
|
+
output_mgr.print(f"[green]✓[/green] Promoted App to v{version_number}!")
|
|
402
|
+
output_mgr.print(f"\nView Deployment: [magenta]{resp.url}[/magenta]")
|
|
403
|
+
|
|
404
|
+
|
|
354
405
|
@app_cli.command("rollback", no_args_is_help=True, context_settings={"ignore_unknown_options": True})
|
|
355
406
|
@click.argument("app_identifier")
|
|
356
407
|
@click.argument("version", default="")
|
|
@@ -3,7 +3,8 @@ import json
|
|
|
3
3
|
|
|
4
4
|
import click
|
|
5
5
|
|
|
6
|
-
from modal
|
|
6
|
+
from modal import config as config_module
|
|
7
|
+
from modal.config import _store_user_config, config
|
|
7
8
|
from modal.environments import Environment
|
|
8
9
|
from modal.output import OutputManager
|
|
9
10
|
|
|
@@ -46,7 +47,7 @@ def set_environment(environment_name: str):
|
|
|
46
47
|
# Confirm that the environment exists by looking it up
|
|
47
48
|
Environment.from_name(environment_name).hydrate()
|
|
48
49
|
_store_user_config({"environment": environment_name})
|
|
49
|
-
click.echo(f"New default environment for profile {_profile}: {environment_name}")
|
|
50
|
+
click.echo(f"New default environment for profile {config_module._profile}: {environment_name}")
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
@config_cli.command("set", hidden=True, no_args_is_help=True)
|
|
@@ -8,7 +8,7 @@ from modal._utils.async_utils import synchronizer
|
|
|
8
8
|
from modal.output import OutputManager
|
|
9
9
|
|
|
10
10
|
from . import run, shell as shell_module
|
|
11
|
-
from ._help import ModalCommand, ModalGroup
|
|
11
|
+
from ._help import ModalCommand, ModalGroup, ModalProfileOption
|
|
12
12
|
from .app import app_cli
|
|
13
13
|
from .billing import billing_cli
|
|
14
14
|
from .bootstrap import bootstrap
|
|
@@ -52,6 +52,12 @@ from .workspace import workspace_cli
|
|
|
52
52
|
expose_value=False,
|
|
53
53
|
callback=lambda ctx, param, value: _version_callback(ctx, value),
|
|
54
54
|
)
|
|
55
|
+
@click.option(
|
|
56
|
+
"--profile",
|
|
57
|
+
cls=ModalProfileOption,
|
|
58
|
+
environment_variable="MODAL_PROFILE",
|
|
59
|
+
help="Use this Modal profile for the command.",
|
|
60
|
+
)
|
|
55
61
|
def entrypoint_cli():
|
|
56
62
|
pass
|
|
57
63
|
|
|
@@ -88,14 +94,13 @@ def check_path():
|
|
|
88
94
|
|
|
89
95
|
|
|
90
96
|
@click.command("setup", cls=ModalCommand, help="Bootstrap Modal's configuration.")
|
|
91
|
-
@click.option("--profile", default=None)
|
|
92
97
|
@synchronizer.create_blocking
|
|
93
|
-
async def setup(
|
|
98
|
+
async def setup():
|
|
94
99
|
check_path()
|
|
95
100
|
print_logo()
|
|
96
101
|
|
|
97
102
|
# Fetch a new token (same as `modal token new` but redirect to /home once finishes)
|
|
98
|
-
await _new_token(
|
|
103
|
+
await _new_token(next_url="/home")
|
|
99
104
|
|
|
100
105
|
output = OutputManager.get()
|
|
101
106
|
output.print("[green]→[/green] Run [bold]modal skills install[/bold] to install agent skills")
|
|
@@ -72,10 +72,26 @@ def list_(json: bool = False):
|
|
|
72
72
|
@environment_cli.command("create", help="Create a new environment in the current workspace.", no_args_is_help=True)
|
|
73
73
|
@click.argument("name")
|
|
74
74
|
@click.option("--restricted", is_flag=True, default=False, help="Enable RBAC restrictions on the new environment")
|
|
75
|
+
@click.option(
|
|
76
|
+
"--default-role",
|
|
77
|
+
type=str,
|
|
78
|
+
default=None,
|
|
79
|
+
help="Default member Role for the Restricted Environment",
|
|
80
|
+
)
|
|
75
81
|
@click.option("--public", is_flag=True, default=False, hidden=True)
|
|
76
|
-
def create(
|
|
82
|
+
def create(
|
|
83
|
+
name: str,
|
|
84
|
+
restricted: bool = False,
|
|
85
|
+
default_role: str | None = None,
|
|
86
|
+
public: bool = False,
|
|
87
|
+
):
|
|
77
88
|
check_environment_name(name)
|
|
78
|
-
Environment.objects.create(
|
|
89
|
+
Environment.objects.create(
|
|
90
|
+
name,
|
|
91
|
+
restricted=restricted,
|
|
92
|
+
default_role=default_role,
|
|
93
|
+
experimental_options={"is_public": public},
|
|
94
|
+
)
|
|
79
95
|
prefix = "Public " if public else "Restricted " if restricted else ""
|
|
80
96
|
rich.print(f"[green]✓[/green] {prefix}Environment created: {name}")
|
|
81
97
|
|
|
@@ -8,11 +8,11 @@ import click
|
|
|
8
8
|
from rich.json import JSON
|
|
9
9
|
from rich.table import Table
|
|
10
10
|
|
|
11
|
+
from modal import config as config_module
|
|
11
12
|
from modal._utils.async_utils import synchronizer
|
|
12
13
|
from modal.config import (
|
|
13
14
|
Config,
|
|
14
15
|
_lookup_workspace,
|
|
15
|
-
_profile,
|
|
16
16
|
config_profiles,
|
|
17
17
|
config_set_active_profile,
|
|
18
18
|
)
|
|
@@ -41,7 +41,7 @@ def activate(profile: str):
|
|
|
41
41
|
|
|
42
42
|
@profile_cli.command("current", help="Print the currently active Modal profile.")
|
|
43
43
|
def current():
|
|
44
|
-
click.echo(_profile)
|
|
44
|
+
click.echo(config_module._profile)
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
@profile_cli.command("list", help="Show all Modal profiles and highlight the active one.")
|
|
@@ -62,7 +62,7 @@ async def list_(json: bool | None = False):
|
|
|
62
62
|
|
|
63
63
|
rows = []
|
|
64
64
|
for profile, resp in zip(profiles, responses):
|
|
65
|
-
active = profile == _profile
|
|
65
|
+
active = profile == config_module._profile
|
|
66
66
|
if isinstance(resp, AuthError):
|
|
67
67
|
workspace = "Unknown (authentication failure)"
|
|
68
68
|
elif isinstance(resp, TimeoutError):
|
|
@@ -80,7 +80,7 @@ async def list_(json: bool | None = False):
|
|
|
80
80
|
if "MODAL_TOKEN_ID" in os.environ:
|
|
81
81
|
try:
|
|
82
82
|
env_based_resp = await _lookup_workspace(
|
|
83
|
-
config.get("server_url", profile=_profile),
|
|
83
|
+
config.get("server_url", profile=config_module._profile),
|
|
84
84
|
os.environ["MODAL_TOKEN_ID"],
|
|
85
85
|
os.environ.get("MODAL_TOKEN_SECRET", ""),
|
|
86
86
|
)
|
|
@@ -23,7 +23,7 @@ from ..functions import Function
|
|
|
23
23
|
from ..output import OutputManager
|
|
24
24
|
from ..runner import DEPLOYMENT_STRATEGY_TYPE, deploy_app, run_app
|
|
25
25
|
from ..serving import serve_app
|
|
26
|
-
from ._help import ModalCommand, ModalGroup
|
|
26
|
+
from ._help import ModalCommand, ModalGroup, _consume_global_options, _global_option_token_length
|
|
27
27
|
from .import_refs import (
|
|
28
28
|
CLICommand,
|
|
29
29
|
MethodReference,
|
|
@@ -411,6 +411,33 @@ def _get_runnable_list(all_usable_commands: list[CLICommand]) -> str:
|
|
|
411
411
|
class RunGroup(ModalGroup):
|
|
412
412
|
"""Click group that resolves subcommands dynamically from a file/module ref."""
|
|
413
413
|
|
|
414
|
+
defer_global_option_parsing = True
|
|
415
|
+
|
|
416
|
+
def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
|
|
417
|
+
option_params: dict[str, click.Option] = {}
|
|
418
|
+
for param in self.get_params(ctx):
|
|
419
|
+
if isinstance(param, click.Option):
|
|
420
|
+
for option in (*param.opts, *param.secondary_opts):
|
|
421
|
+
option_params[option] = param
|
|
422
|
+
|
|
423
|
+
func_ref_index = 0
|
|
424
|
+
while func_ref_index < len(args):
|
|
425
|
+
arg = args[func_ref_index]
|
|
426
|
+
if global_option_length := _global_option_token_length(ctx, args, func_ref_index):
|
|
427
|
+
func_ref_index += global_option_length
|
|
428
|
+
elif arg == "--":
|
|
429
|
+
func_ref_index += 1
|
|
430
|
+
break
|
|
431
|
+
elif param := option_params.get(arg.split("=", 1)[0]):
|
|
432
|
+
func_ref_index += 1 if "=" in arg or param.is_flag else param.nargs + 1
|
|
433
|
+
elif arg.startswith("-"):
|
|
434
|
+
func_ref_index += 1
|
|
435
|
+
else:
|
|
436
|
+
break
|
|
437
|
+
|
|
438
|
+
args[:] = _consume_global_options(ctx, args[:func_ref_index]) + args[func_ref_index:]
|
|
439
|
+
return click.Group.parse_args(self, ctx, args)
|
|
440
|
+
|
|
414
441
|
def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None:
|
|
415
442
|
# note: get_command here is run before the "group logic" in the `run` logic below
|
|
416
443
|
# so to ensure that `env` has been globally populated before user code is loaded, it
|
|
@@ -15,11 +15,6 @@ from ._help import ModalGroup
|
|
|
15
15
|
|
|
16
16
|
token_cli = ModalGroup(name="token", help="Manage tokens.")
|
|
17
17
|
|
|
18
|
-
_PROFILE_HELP = (
|
|
19
|
-
"Modal profile to set credentials for. If unspecified "
|
|
20
|
-
"(and MODAL_PROFILE environment variable is not set), "
|
|
21
|
-
"uses the workspace name associated with the credentials."
|
|
22
|
-
)
|
|
23
18
|
_ACTIVATE_HELP = "Activate the profile containing this token after creation."
|
|
24
19
|
_VERIFY_HELP = "Make a test request to verify the new credentials."
|
|
25
20
|
|
|
@@ -27,14 +22,12 @@ _VERIFY_HELP = "Make a test request to verify the new credentials."
|
|
|
27
22
|
@token_cli.command("set")
|
|
28
23
|
@click.option("--token-id", default=None, help="Account token ID.")
|
|
29
24
|
@click.option("--token-secret", default=None, help="Account token secret.")
|
|
30
|
-
@click.option("--profile", default=None, help=_PROFILE_HELP)
|
|
31
25
|
@click.option("--activate/--no-activate", default=True, help=_ACTIVATE_HELP)
|
|
32
26
|
@click.option("--verify/--no-verify", default=True, help=_VERIFY_HELP)
|
|
33
27
|
@synchronizer.create_blocking
|
|
34
28
|
async def set(
|
|
35
29
|
token_id: str | None,
|
|
36
30
|
token_secret: str | None,
|
|
37
|
-
profile: str | None,
|
|
38
31
|
activate: bool,
|
|
39
32
|
verify: bool,
|
|
40
33
|
):
|
|
@@ -46,18 +39,17 @@ async def set(
|
|
|
46
39
|
token_id = getpass.getpass("Token ID:")
|
|
47
40
|
if token_secret is None:
|
|
48
41
|
token_secret = getpass.getpass("Token secret:")
|
|
49
|
-
await _set_token(token_id, token_secret,
|
|
42
|
+
await _set_token(token_id, token_secret, activate=activate, verify=verify)
|
|
50
43
|
|
|
51
44
|
|
|
52
45
|
@token_cli.command("new")
|
|
53
|
-
@click.option("--profile", default=None, help=_PROFILE_HELP)
|
|
54
46
|
@click.option("--activate/--no-activate", default=True, help=_ACTIVATE_HELP)
|
|
55
47
|
@click.option("--verify/--no-verify", default=True, help=_VERIFY_HELP)
|
|
56
48
|
@click.option("--source", default=None, hidden=True)
|
|
57
49
|
@synchronizer.create_blocking
|
|
58
|
-
async def new(
|
|
50
|
+
async def new(activate: bool, verify: bool, source: str | None):
|
|
59
51
|
"""Create a new token by using an authenticated web session."""
|
|
60
|
-
await _new_token(
|
|
52
|
+
await _new_token(activate=activate, verify=verify, source=source)
|
|
61
53
|
|
|
62
54
|
|
|
63
55
|
@token_cli.command("info")
|
|
@@ -17,6 +17,7 @@ from modal._utils.async_utils import synchronizer
|
|
|
17
17
|
from modal_proto import api_pb2, modal_api_grpc
|
|
18
18
|
from modal_version import __version__
|
|
19
19
|
|
|
20
|
+
from . import config as config_module
|
|
20
21
|
from ._traceback import print_server_warnings
|
|
21
22
|
from ._utils import async_utils
|
|
22
23
|
from ._utils.async_utils import TaskContext, synchronize_api
|
|
@@ -240,6 +241,13 @@ class _Client:
|
|
|
240
241
|
client_type = api_pb2.CLIENT_TYPE_CLIENT
|
|
241
242
|
credentials = (token_id, token_secret)
|
|
242
243
|
else:
|
|
244
|
+
profile = config_module._profile
|
|
245
|
+
if os.environ.get("MODAL_PROFILE") and profile not in config_module._user_config:
|
|
246
|
+
raise AuthError(
|
|
247
|
+
f"Modal profile '{profile}' was not found in {config_module.user_config_path}. "
|
|
248
|
+
"Run `modal profile list` to see available profiles, or "
|
|
249
|
+
f"`modal token new --profile {profile}` to configure it."
|
|
250
|
+
)
|
|
243
251
|
raise AuthError(
|
|
244
252
|
"Token missing. Could not authenticate client."
|
|
245
253
|
" If you have token credentials, see modal.com/docs/sdk/py/latest/config for setup help."
|
|
@@ -38,7 +38,7 @@ class _Client:
|
|
|
38
38
|
server_url: str,
|
|
39
39
|
client_type: int,
|
|
40
40
|
credentials: typing.Optional[tuple[str, str]],
|
|
41
|
-
version: str = "1.5.5.
|
|
41
|
+
version: str = "1.5.5.dev6",
|
|
42
42
|
):
|
|
43
43
|
"""mdmd:hidden
|
|
44
44
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -209,7 +209,7 @@ class Client:
|
|
|
209
209
|
server_url: str,
|
|
210
210
|
client_type: int,
|
|
211
211
|
credentials: typing.Optional[tuple[str, str]],
|
|
212
|
-
version: str = "1.5.5.
|
|
212
|
+
version: str = "1.5.5.dev6",
|
|
213
213
|
):
|
|
214
214
|
"""mdmd:hidden
|
|
215
215
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -242,6 +242,13 @@ def _check_config() -> None:
|
|
|
242
242
|
|
|
243
243
|
_profile = os.environ.get("MODAL_PROFILE") or _config_active_profile()
|
|
244
244
|
|
|
245
|
+
|
|
246
|
+
def _set_profile(profile: str) -> None:
|
|
247
|
+
os.environ["MODAL_PROFILE"] = profile
|
|
248
|
+
global _profile
|
|
249
|
+
_profile = profile
|
|
250
|
+
|
|
251
|
+
|
|
245
252
|
# Define settings
|
|
246
253
|
|
|
247
254
|
|
|
@@ -23,6 +23,7 @@ class EnvironmentManager:
|
|
|
23
23
|
name: str,
|
|
24
24
|
*,
|
|
25
25
|
restricted: bool = False,
|
|
26
|
+
default_role: typing.Optional[str] = None,
|
|
26
27
|
experimental_options: typing.Optional[dict[str, typing.Any]] = None,
|
|
27
28
|
client: typing.Optional[modal.client.Client] = None,
|
|
28
29
|
) -> None:
|
|
@@ -42,6 +43,7 @@ class EnvironmentManager:
|
|
|
42
43
|
name: str,
|
|
43
44
|
*,
|
|
44
45
|
restricted: bool = False,
|
|
46
|
+
default_role: typing.Optional[str] = None,
|
|
45
47
|
experimental_options: typing.Optional[dict[str, typing.Any]] = None,
|
|
46
48
|
client: typing.Optional[modal.client.Client] = None,
|
|
47
49
|
) -> None:
|
|
@@ -105,6 +105,7 @@ async def list_deployed_apps(environment_name: str = "", client: _Client | None
|
|
|
105
105
|
class AppLifecycle:
|
|
106
106
|
"""Lifecycle information about an App."""
|
|
107
107
|
|
|
108
|
+
version: int | None # Current version of a deployed App, or None for an ephemeral App
|
|
108
109
|
created_at: datetime # Time the App was initially created
|
|
109
110
|
created_by: str # User or service user name
|
|
110
111
|
deployed_at: datetime | None # Time of the most recent deployment event, if ever deployed
|
|
@@ -132,6 +133,7 @@ async def get_app_lifecycle(app_id: str, *, client: _Client | None = None) -> Ap
|
|
|
132
133
|
)
|
|
133
134
|
lifecycle = resp.lifecycle
|
|
134
135
|
return AppLifecycle(
|
|
136
|
+
version=lifecycle.version or None,
|
|
135
137
|
created_at=datetime.fromtimestamp(lifecycle.created_at, timezone.utc),
|
|
136
138
|
created_by=lifecycle.created_by,
|
|
137
139
|
deployed_at=_timestamp_to_datetime(lifecycle.deployed_at),
|
|
@@ -615,6 +615,10 @@ class ModalClientBase(abc.ABC):
|
|
|
615
615
|
async def SandboxGetExitSnapshot(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.SandboxGetExitSnapshotRequest, modal_proto.api_pb2.SandboxGetExitSnapshotResponse]') -> None:
|
|
616
616
|
pass
|
|
617
617
|
|
|
618
|
+
@abc.abstractmethod
|
|
619
|
+
async def SandboxGetExitSnapshotV2(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.SandboxGetExitSnapshotRequest, modal_proto.api_pb2.SandboxGetExitSnapshotResponse]') -> None:
|
|
620
|
+
pass
|
|
621
|
+
|
|
618
622
|
@abc.abstractmethod
|
|
619
623
|
async def SandboxGetFromName(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.SandboxGetFromNameRequest, modal_proto.api_pb2.SandboxGetFromNameResponse]') -> None:
|
|
620
624
|
pass
|
|
@@ -1871,6 +1875,12 @@ class ModalClientBase(abc.ABC):
|
|
|
1871
1875
|
modal_proto.api_pb2.SandboxGetExitSnapshotRequest,
|
|
1872
1876
|
modal_proto.api_pb2.SandboxGetExitSnapshotResponse,
|
|
1873
1877
|
),
|
|
1878
|
+
'/modal.client.ModalClient/SandboxGetExitSnapshotV2': grpclib.const.Handler(
|
|
1879
|
+
self.SandboxGetExitSnapshotV2,
|
|
1880
|
+
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1881
|
+
modal_proto.api_pb2.SandboxGetExitSnapshotRequest,
|
|
1882
|
+
modal_proto.api_pb2.SandboxGetExitSnapshotResponse,
|
|
1883
|
+
),
|
|
1874
1884
|
'/modal.client.ModalClient/SandboxGetFromName': grpclib.const.Handler(
|
|
1875
1885
|
self.SandboxGetFromName,
|
|
1876
1886
|
grpclib.const.Cardinality.UNARY_UNARY,
|
|
@@ -3311,6 +3321,12 @@ class ModalClientStub:
|
|
|
3311
3321
|
modal_proto.api_pb2.SandboxGetExitSnapshotRequest,
|
|
3312
3322
|
modal_proto.api_pb2.SandboxGetExitSnapshotResponse,
|
|
3313
3323
|
)
|
|
3324
|
+
self.SandboxGetExitSnapshotV2 = grpclib.client.UnaryUnaryMethod(
|
|
3325
|
+
channel,
|
|
3326
|
+
'/modal.client.ModalClient/SandboxGetExitSnapshotV2',
|
|
3327
|
+
modal_proto.api_pb2.SandboxGetExitSnapshotRequest,
|
|
3328
|
+
modal_proto.api_pb2.SandboxGetExitSnapshotResponse,
|
|
3329
|
+
)
|
|
3314
3330
|
self.SandboxGetFromName = grpclib.client.UnaryUnaryMethod(
|
|
3315
3331
|
channel,
|
|
3316
3332
|
'/modal.client.ModalClient/SandboxGetFromName',
|