modal 1.5.3.dev13__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.dev13 → modal-1.5.4.dev0}/PKG-INFO +1 -1
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_clustered_functions.py +11 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_clustered_functions.pyi +6 -2
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_environments.py +66 -36
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/environment.py +103 -41
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/client.pyi +2 -2
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/environments.py +3 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/environments.pyi +68 -53
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/experimental/__init__.py +9 -1
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/api_pb2.py +274 -274
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/api_pb2.pyi +6 -1
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_version/__init__.py +1 -1
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/LICENSE +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/README.md +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/__init__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/__main__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_billing.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_container_entrypoint.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_function_variants.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_functions.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_grpc_client.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_image.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_ipython.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_load_context.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_location.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_logs.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_logs_manager.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_logs_manager.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_object.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_output/__init__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_output/manager.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_output/pty.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_output/rich.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_output/status.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_partial_function.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_resolver.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_resources.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/__init__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/asgi.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/execution_context.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/telemetry.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_serialization.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_server.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_supports_logs.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_traceback.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_tunnel.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_tunnel.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_type_manager.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/__init__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/app_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/async_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/blob_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/browser_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/curl_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/deprecation.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/display_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/docker_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/function_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/git_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/hash_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/http_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/logger.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/mount_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/name_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/package_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/proxy_support.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/shell_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/time_utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_vendor/__init__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_vendor/tblib.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_vendor/version.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_watcher.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_workspace.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/app.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/app.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/billing.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/billing.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/2023.12.txt +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/2024.04.txt +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/2024.10.txt +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/2025.06.txt +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/README.md +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/base-images.json +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/call_graph.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/__init__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/_download.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/_help.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/_traceback.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/app.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/billing.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/bootstrap.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/changelog.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/cluster.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/config.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/container.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/curl.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/dashboard.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/dict.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/endpoint.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/entry_point.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/image.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/import_refs.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/launch.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/logo.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/network_file_system.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/profile.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/programs/__init__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/programs/vscode.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/queues.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/run.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/secret.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/selector.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/shell.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/skills.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/token.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/utils.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/volume.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/workspace.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/client.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cls.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cls.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/config.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/container_process.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/container_process.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/dict.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/dict.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/exception.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/experimental/flash.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/experimental/flash.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/experimental/ipython.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/file_io.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/file_io.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/file_pattern_matcher.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/functions.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/functions.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/image.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/image.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/io_streams.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/io_streams.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/mount.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/mount.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/network_file_system.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/network_file_system.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/object.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/object.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/output.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/parallel_map.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/parallel_map.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/partial_function.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/partial_function.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/proxy.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/proxy.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/py.typed +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/queue.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/queue.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/retries.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/runner.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/runner.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/running_app.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/sandbox.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/sandbox.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/sandbox_fs.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/sandbox_fs.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/schedule.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/scheduler_placement.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/secret.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/secret.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/server.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/server.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/serving.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/serving.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/snapshot.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/snapshot.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/stream_type.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/token_flow.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/token_flow.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/types.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/volume.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/volume.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/workspace.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/workspace.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/requires.txt +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/top_level.txt +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/__init__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/mdmd/types.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/reference_intro.md +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/__init__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/api_grpc.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/py.typed +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_version/__main__.py +0 -0
- {modal-1.5.3.dev13 → modal-1.5.4.dev0}/pyproject.toml +0 -0
- {modal-1.5.3.dev13 → 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
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Copyright Modal Labs 2023
|
|
2
2
|
import json as json_mod
|
|
3
|
+
import warnings
|
|
3
4
|
|
|
4
5
|
import click
|
|
5
6
|
import rich
|
|
@@ -11,6 +12,7 @@ from rich.text import Text
|
|
|
11
12
|
from modal import environments
|
|
12
13
|
from modal._environments import _Environment
|
|
13
14
|
from modal._utils.async_utils import synchronizer
|
|
15
|
+
from modal._utils.deprecation import deprecation_warning
|
|
14
16
|
from modal._utils.display_utils import pretty_decimal
|
|
15
17
|
from modal._utils.name_utils import check_environment_name
|
|
16
18
|
from modal._utils.time_utils import format_interval
|
|
@@ -18,7 +20,7 @@ from modal.cli.billing import DATE_HELP, _validate_and_parse_interval
|
|
|
18
20
|
from modal.cli.utils import _col_name_to_json_key, confirm_or_suggest_yes, display_table, yes_option
|
|
19
21
|
from modal.config import config
|
|
20
22
|
from modal.environments import Environment
|
|
21
|
-
from modal.exception import InvalidError
|
|
23
|
+
from modal.exception import DeprecationError, InvalidError
|
|
22
24
|
from modal.output import OutputManager
|
|
23
25
|
|
|
24
26
|
from ._help import ModalGroup
|
|
@@ -55,7 +57,7 @@ def list_(json: bool = False):
|
|
|
55
57
|
# over the server default
|
|
56
58
|
active_env = config.get("environment") or ""
|
|
57
59
|
for env in envs:
|
|
58
|
-
if env.default is True and active_env
|
|
60
|
+
if env.default is True and not active_env:
|
|
59
61
|
active_env = env.name
|
|
60
62
|
|
|
61
63
|
table_data = []
|
|
@@ -123,67 +125,127 @@ def update(
|
|
|
123
125
|
rich.print("[green]✓[/green] Environment updated")
|
|
124
126
|
|
|
125
127
|
|
|
126
|
-
MEMBERS_HELP_TEXT = """Manage members and their roles in a restricted Environment.
|
|
127
|
-
|
|
128
|
-
Restricted Environments use RBAC to limit the actions that can be performed by
|
|
129
|
-
users (and service users) based on roles: https://modal.com/docs/guide/rbac.
|
|
130
|
-
"""
|
|
131
|
-
|
|
132
|
-
members_cli = ModalGroup(name="members", help=MEMBERS_HELP_TEXT)
|
|
133
|
-
environment_cli.add_command(members_cli)
|
|
134
|
-
|
|
135
128
|
service_user_option = click.option(
|
|
136
|
-
"--service-user", is_flag=True, default=False, help="Treat
|
|
129
|
+
"--service-user", is_flag=True, default=False, help="Treat PRINCIPAL as the name of a service user"
|
|
137
130
|
)
|
|
138
131
|
|
|
139
132
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
@click.option("--json", is_flag=True, default=False)
|
|
143
|
-
def members_list(environment: str, json: bool = False):
|
|
144
|
-
members = Environment.from_name(environment).members.list()
|
|
133
|
+
def _render_roles_list(environment: str, json: bool) -> None:
|
|
134
|
+
roles = Environment.from_name(environment).roles.list()
|
|
145
135
|
if json:
|
|
146
136
|
# Mirror the API output shape: a nested dict keyed by users / service_users.
|
|
147
|
-
OutputManager.get().print_json(json_mod.dumps(
|
|
137
|
+
OutputManager.get().print_json(json_mod.dumps(roles))
|
|
148
138
|
return
|
|
149
139
|
rows = []
|
|
150
|
-
for name, role in
|
|
140
|
+
for name, role in roles.get("users", {}).items():
|
|
151
141
|
rows.append([name, role])
|
|
152
|
-
for name, role in
|
|
142
|
+
for name, role in roles.get("service_users", {}).items():
|
|
153
143
|
rows.append([f"{name} (service user)", role])
|
|
154
144
|
rows = sorted(rows, key=lambda x: x[0])
|
|
155
145
|
display_table(["name", "role"], rows)
|
|
156
146
|
|
|
157
147
|
|
|
158
|
-
|
|
159
|
-
@click.argument("environment")
|
|
160
|
-
@click.argument("member")
|
|
161
|
-
@click.option(
|
|
162
|
-
"--role", type=click.Choice(["contributor", "viewer"]), required=True, help="Role to assign to the member"
|
|
163
|
-
)
|
|
164
|
-
@service_user_option
|
|
165
|
-
def members_update(environment: str, member: str, role: str, service_user: bool = False):
|
|
148
|
+
def _apply_role_update(environment: str, principal: str, role: str, service_user: bool) -> None:
|
|
166
149
|
env = Environment.from_name(environment)
|
|
167
150
|
if service_user:
|
|
168
|
-
env.
|
|
151
|
+
env.roles.update(service_users={principal: role})
|
|
169
152
|
else:
|
|
170
|
-
env.
|
|
153
|
+
env.roles.update(users={principal: role})
|
|
171
154
|
kind = "service user" if service_user else "user"
|
|
172
|
-
rich.print(f"[green]✓[/green] Set {kind} {
|
|
155
|
+
rich.print(f"[green]✓[/green] Set {kind} {principal!r} to role {role!r} in environment {environment!r}")
|
|
173
156
|
|
|
174
157
|
|
|
175
|
-
|
|
176
|
-
@click.argument("environment")
|
|
177
|
-
@click.argument("member")
|
|
178
|
-
@service_user_option
|
|
179
|
-
def members_remove(environment: str, member: str, service_user: bool = False):
|
|
158
|
+
def _apply_role_remove(environment: str, principal: str, service_user: bool) -> None:
|
|
180
159
|
env = Environment.from_name(environment)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
160
|
+
# `remove` only lives on the deprecated `members` manager, which warns. The caller (a
|
|
161
|
+
# deprecated `members` command) has already warned, so suppress the redundant SDK warning.
|
|
162
|
+
with warnings.catch_warnings():
|
|
163
|
+
warnings.simplefilter("ignore", DeprecationError)
|
|
164
|
+
if service_user:
|
|
165
|
+
env.members.remove(service_users=[principal])
|
|
166
|
+
else:
|
|
167
|
+
env.members.remove(users=[principal])
|
|
185
168
|
kind = "service user" if service_user else "user"
|
|
186
|
-
rich.print(f"[green]✓[/green] Removed {kind} {
|
|
169
|
+
rich.print(f"[green]✓[/green] Removed {kind} {principal!r} from environment {environment!r}")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
ROLES_HELP_TEXT = """Manage the Environment Roles of users and service users.
|
|
173
|
+
|
|
174
|
+
An Environment Role is one of 'contributor' (read-write), 'viewer' (read-only), or
|
|
175
|
+
'no-access', and dictates access to Environments. See
|
|
176
|
+
https://modal.com/docs/guide/rbac for details on which principals can be assigned
|
|
177
|
+
which roles.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
roles_cli = ModalGroup(name="roles", help=ROLES_HELP_TEXT)
|
|
181
|
+
environment_cli.add_command(roles_cli)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@roles_cli.command("list", help="List the roles of each user and service user in an Environment", no_args_is_help=True)
|
|
185
|
+
@click.argument("environment")
|
|
186
|
+
@click.option("--json", is_flag=True, default=False)
|
|
187
|
+
def roles_list(environment: str, json: bool = False):
|
|
188
|
+
_render_roles_list(environment, json)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@roles_cli.command("update", help="Update a user's or service user's role in an Environment", no_args_is_help=True)
|
|
192
|
+
@click.argument("environment")
|
|
193
|
+
@click.argument("principal")
|
|
194
|
+
@click.option("--role", type=click.Choice(["contributor", "viewer", "no-access"]), required=True, help="Role to assign")
|
|
195
|
+
@service_user_option
|
|
196
|
+
def roles_update(environment: str, principal: str, role: str, service_user: bool = False):
|
|
197
|
+
_apply_role_update(environment, principal, role, service_user)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# The deprecated `members` command group keeps working for existing automation, but is hidden
|
|
201
|
+
# from `--help` so it is not discoverable. Each subcommand warns with its own guidance.
|
|
202
|
+
members_cli = ModalGroup(name="members", help="[Deprecated] Use `modal environment roles`.", hidden=True)
|
|
203
|
+
environment_cli.add_command(members_cli)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@members_cli.command("list", help="[Deprecated] Use `modal environment roles list`", no_args_is_help=True)
|
|
207
|
+
@click.argument("environment")
|
|
208
|
+
@click.option("--json", is_flag=True, default=False)
|
|
209
|
+
def members_list(environment: str, json: bool = False):
|
|
210
|
+
deprecation_warning(
|
|
211
|
+
(2026, 7, 23),
|
|
212
|
+
"`modal environment members list` is deprecated; use `modal environment roles list`.",
|
|
213
|
+
show_source=False,
|
|
214
|
+
)
|
|
215
|
+
_render_roles_list(environment, json)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@members_cli.command("update", help="[Deprecated] Use `modal environment roles update`", no_args_is_help=True)
|
|
219
|
+
@click.argument("environment")
|
|
220
|
+
@click.argument("principal")
|
|
221
|
+
@click.option(
|
|
222
|
+
"--role",
|
|
223
|
+
type=click.Choice(["contributor", "viewer", "no-access"]),
|
|
224
|
+
required=True,
|
|
225
|
+
help="Role to assign to the principal",
|
|
226
|
+
)
|
|
227
|
+
@service_user_option
|
|
228
|
+
def members_update(environment: str, principal: str, role: str, service_user: bool = False):
|
|
229
|
+
deprecation_warning(
|
|
230
|
+
(2026, 7, 23),
|
|
231
|
+
"`modal environment members update` is deprecated; use `modal environment roles update`.",
|
|
232
|
+
show_source=False,
|
|
233
|
+
)
|
|
234
|
+
_apply_role_update(environment, principal, role, service_user)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@members_cli.command("remove", help="[Deprecated] Use `modal environment roles update`", no_args_is_help=True)
|
|
238
|
+
@click.argument("environment")
|
|
239
|
+
@click.argument("principal")
|
|
240
|
+
@service_user_option
|
|
241
|
+
def members_remove(environment: str, principal: str, service_user: bool = False):
|
|
242
|
+
deprecation_warning(
|
|
243
|
+
(2026, 7, 23),
|
|
244
|
+
"`modal environment members remove` is deprecated; roles are now explicit — set a role "
|
|
245
|
+
"(e.g. `--role no-access`) with `modal environment roles update` instead.",
|
|
246
|
+
show_source=False,
|
|
247
|
+
)
|
|
248
|
+
_apply_role_remove(environment, principal, service_user)
|
|
187
249
|
|
|
188
250
|
|
|
189
251
|
billing_cli = ModalGroup(name="billing", help="View billing and usage info for the given Environment.")
|
|
@@ -37,7 +37,7 @@ class _Client:
|
|
|
37
37
|
server_url: str,
|
|
38
38
|
client_type: int,
|
|
39
39
|
credentials: typing.Optional[tuple[str, str]],
|
|
40
|
-
version: str = "1.5.
|
|
40
|
+
version: str = "1.5.4.dev0",
|
|
41
41
|
):
|
|
42
42
|
"""mdmd:hidden
|
|
43
43
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -207,7 +207,7 @@ class Client:
|
|
|
207
207
|
server_url: str,
|
|
208
208
|
client_type: int,
|
|
209
209
|
credentials: typing.Optional[tuple[str, str]],
|
|
210
|
-
version: str = "1.5.
|
|
210
|
+
version: str = "1.5.4.dev0",
|
|
211
211
|
):
|
|
212
212
|
"""mdmd:hidden
|
|
213
213
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -6,12 +6,15 @@ from ._environments import (
|
|
|
6
6
|
_EnvironmentBillingManager,
|
|
7
7
|
_EnvironmentManager,
|
|
8
8
|
_EnvironmentMembersManager,
|
|
9
|
+
_EnvironmentRolesManager,
|
|
9
10
|
_list_environments,
|
|
10
11
|
_update_environment,
|
|
11
12
|
)
|
|
12
13
|
from ._utils.async_utils import synchronize_api, synchronizer
|
|
13
14
|
|
|
14
15
|
EnvironmentManager = synchronize_api(_EnvironmentManager, target_module=__name__)
|
|
16
|
+
EnvironmentRolesManager = synchronize_api(_EnvironmentRolesManager, target_module=__name__)
|
|
17
|
+
# Deprecated alias for `EnvironmentRolesManager`, retired with `Environment.members`.
|
|
15
18
|
EnvironmentMembersManager = synchronize_api(_EnvironmentMembersManager, target_module=__name__)
|
|
16
19
|
EnvironmentBillingManager = synchronize_api(_EnvironmentBillingManager, target_module=__name__)
|
|
17
20
|
Environment = synchronize_api(_Environment, target_module=__name__)
|