modal 1.5.2.dev18__tar.gz → 1.5.2.dev20__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.2.dev18 → modal-1.5.2.dev20}/PKG-INFO +1 -1
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_environments.py +6 -9
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_image.py +36 -7
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/grpc_utils.py +20 -12
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/name_utils.py +8 -2
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/task_command_router_client.py +2 -2
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_workspace.py +9 -3
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/app.py +0 -3
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/app.pyi +0 -2
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/container.py +5 -5
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/environment.py +2 -2
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/image.py +8 -1
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/client.pyi +2 -2
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/environments.pyi +6 -14
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/types.py +2 -5
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/workspace.pyi +2 -2
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/api_grpc.py +16 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/api_pb2.py +601 -594
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/api_pb2.pyi +38 -22
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/api_pb2_grpc.py +34 -1
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/api_pb2_grpc.pyi +13 -3
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/modal_api_grpc.py +1 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_version/__init__.py +1 -1
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/LICENSE +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/README.md +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/__init__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/__main__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_billing.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_clustered_functions.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_clustered_functions.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_container_entrypoint.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_function_variants.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_functions.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_grpc_client.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_ipython.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_load_context.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_location.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_logs.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_object.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_output/__init__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_output/manager.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_output/pty.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_output/rich.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_output/status.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_partial_function.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_resolver.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_resources.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/__init__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/asgi.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/execution_context.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/telemetry.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_serialization.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_server.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_traceback.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_tunnel.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_tunnel.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_type_manager.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/__init__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/app_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/async_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/blob_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/browser_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/curl_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/deprecation.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/docker_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/function_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/git_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/hash_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/http_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/logger.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/mount_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/package_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/proxy_support.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/shell_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_utils/time_utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_vendor/__init__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_vendor/tblib.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_vendor/version.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/_watcher.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/billing.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/billing.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/builder/2023.12.txt +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/builder/2024.04.txt +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/builder/2024.10.txt +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/builder/2025.06.txt +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/builder/README.md +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/builder/base-images.json +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/call_graph.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/__init__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/_download.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/_help.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/_traceback.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/app.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/billing.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/bootstrap.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/changelog.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/cluster.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/config.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/curl.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/dashboard.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/dict.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/endpoint.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/entry_point.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/import_refs.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/launch.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/logo.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/network_file_system.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/profile.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/programs/__init__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/programs/vscode.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/queues.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/run.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/secret.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/selector.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/shell.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/skills.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/token.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/utils.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/volume.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cli/workspace.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/client.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cls.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/cls.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/config.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/container_process.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/container_process.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/dict.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/dict.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/environments.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/exception.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/experimental/__init__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/experimental/flash.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/experimental/flash.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/experimental/ipython.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/file_io.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/file_io.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/file_pattern_matcher.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/functions.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/functions.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/image.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/image.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/io_streams.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/io_streams.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/mount.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/mount.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/network_file_system.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/network_file_system.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/object.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/object.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/output.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/parallel_map.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/parallel_map.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/partial_function.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/partial_function.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/proxy.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/proxy.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/py.typed +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/queue.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/queue.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/retries.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/runner.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/runner.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/running_app.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/sandbox.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/sandbox.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/sandbox_fs.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/sandbox_fs.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/schedule.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/scheduler_placement.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/secret.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/secret.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/server.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/server.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/serving.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/serving.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/snapshot.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/snapshot.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/stream_type.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/token_flow.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/token_flow.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/volume.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/volume.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal/workspace.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal.egg-info/requires.txt +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal.egg-info/top_level.txt +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_docs/__init__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_docs/mdmd/types.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/__init__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/py.typed +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/modal_version/__main__.py +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/pyproject.toml +0 -0
- {modal-1.5.2.dev18 → modal-1.5.2.dev20}/setup.cfg +0 -0
|
@@ -103,9 +103,6 @@ class _EnvironmentManager:
|
|
|
103
103
|
await client.stub.EnvironmentDelete(api_pb2.EnvironmentDeleteRequest(name=name))
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
MemberRole = Literal["viewer", "contributor"]
|
|
107
|
-
|
|
108
|
-
|
|
109
106
|
def _role_to_proto(role: str) -> "api_pb2.EnvironmentRole.ValueType":
|
|
110
107
|
match role:
|
|
111
108
|
case "viewer":
|
|
@@ -116,7 +113,7 @@ def _role_to_proto(role: str) -> "api_pb2.EnvironmentRole.ValueType":
|
|
|
116
113
|
raise InvalidError(f"Invalid Environment role: {role!r} (expected 'viewer' or 'contributor')")
|
|
117
114
|
|
|
118
115
|
|
|
119
|
-
def _role_from_proto(proto_value: int) ->
|
|
116
|
+
def _role_from_proto(proto_value: int) -> str:
|
|
120
117
|
match proto_value:
|
|
121
118
|
case api_pb2.ENVIRONMENT_ROLE_VIEWER:
|
|
122
119
|
return "viewer"
|
|
@@ -137,7 +134,7 @@ class _EnvironmentMembersManager:
|
|
|
137
134
|
"""mdmd:hidden"""
|
|
138
135
|
self._environment = environment
|
|
139
136
|
|
|
140
|
-
async def list(self) -> dict[Literal["users", "service_users"], dict[str,
|
|
137
|
+
async def list(self) -> dict[Literal["users", "service_users"], dict[str, str]]:
|
|
141
138
|
"""Return the members of a restricted Environment with their roles.
|
|
142
139
|
|
|
143
140
|
**Examples:**
|
|
@@ -155,8 +152,8 @@ class _EnvironmentMembersManager:
|
|
|
155
152
|
req = api_pb2.EnvironmentGetManagedRequest(environment_id=self._environment.object_id)
|
|
156
153
|
resp = await self._environment.client.stub.EnvironmentGetManaged(req)
|
|
157
154
|
|
|
158
|
-
users: dict[str,
|
|
159
|
-
service_users: dict[str,
|
|
155
|
+
users: dict[str, str] = {}
|
|
156
|
+
service_users: dict[str, str] = {}
|
|
160
157
|
for principal in resp.principal_roles:
|
|
161
158
|
role = _role_from_proto(principal.role)
|
|
162
159
|
if principal.user_id:
|
|
@@ -169,8 +166,8 @@ class _EnvironmentMembersManager:
|
|
|
169
166
|
async def update(
|
|
170
167
|
self,
|
|
171
168
|
*,
|
|
172
|
-
users: Mapping[str,
|
|
173
|
-
service_users: Mapping[str,
|
|
169
|
+
users: Mapping[str, str] | None = None,
|
|
170
|
+
service_users: Mapping[str, str] | None = None,
|
|
174
171
|
) -> None:
|
|
175
172
|
"""Add or modify roles for members of a restricted Environment.
|
|
176
173
|
|
|
@@ -117,13 +117,34 @@ def _validate_image_tag(tag: str) -> None:
|
|
|
117
117
|
check_object_name(tag, "Image tag")
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
def _parse_named_image_ref(name: str) -> str:
|
|
120
|
+
def _parse_named_image_ref(name: str) -> tuple[str, str]:
|
|
121
|
+
"""Parse an image reference, returning (namespace_prefix, name_tag).
|
|
122
|
+
|
|
123
|
+
If the name contains a '/', the part before the last '/' is extracted as
|
|
124
|
+
a namespace prefix (intended for environment/name or workspace/env/name
|
|
125
|
+
syntax). The actual image name (after the last '/') is validated as a
|
|
126
|
+
standard object name.
|
|
127
|
+
|
|
128
|
+
Returns a tuple of (prefix, "full_name:tag") where prefix is empty string
|
|
129
|
+
if no '/' is present.
|
|
130
|
+
"""
|
|
121
131
|
image_name, sep, tag = name.partition(":")
|
|
122
132
|
if not sep:
|
|
123
133
|
tag = "latest"
|
|
134
|
+
|
|
135
|
+
prefix = ""
|
|
136
|
+
if "/" in image_name:
|
|
137
|
+
prefix, image_name = image_name.rsplit("/", 1)
|
|
138
|
+
if not prefix:
|
|
139
|
+
raise InvalidError("Invalid Image name: '/' prefix must be non-empty.")
|
|
140
|
+
if not image_name:
|
|
141
|
+
raise InvalidError("Invalid Image name: name after '/' must be non-empty.")
|
|
142
|
+
|
|
124
143
|
_validate_image_name(image_name)
|
|
125
144
|
_validate_image_tag(tag)
|
|
126
|
-
|
|
145
|
+
|
|
146
|
+
full_name = f"{prefix}/{image_name}" if prefix else image_name
|
|
147
|
+
return prefix, f"{full_name}:{tag}"
|
|
127
148
|
|
|
128
149
|
|
|
129
150
|
def _validate_python_version(
|
|
@@ -2923,12 +2944,15 @@ class _Image(_Object, type_prefix="im"):
|
|
|
2923
2944
|
...
|
|
2924
2945
|
```
|
|
2925
2946
|
"""
|
|
2926
|
-
tag = _parse_named_image_ref(name)
|
|
2947
|
+
namespace_prefix, tag = _parse_named_image_ref(name)
|
|
2948
|
+
|
|
2949
|
+
if namespace_prefix and environment_name:
|
|
2950
|
+
raise InvalidError("Cannot specify 'environment_name' when the image name contains a '/'.")
|
|
2927
2951
|
|
|
2928
2952
|
async def _load(self: _Image, resolver: Resolver, load_context: LoadContext, existing_object_id: str | None):
|
|
2929
2953
|
req = api_pb2.ImageGetByTagRequest(
|
|
2930
2954
|
tag=tag,
|
|
2931
|
-
environment_name=load_context.environment_name,
|
|
2955
|
+
environment_name="" if namespace_prefix else load_context.environment_name,
|
|
2932
2956
|
)
|
|
2933
2957
|
response = await load_context.client.stub.ImageGetByTag(req)
|
|
2934
2958
|
self._hydrate(response.image_id, load_context.client, None)
|
|
@@ -2963,15 +2987,20 @@ class _Image(_Object, type_prefix="im"):
|
|
|
2963
2987
|
image.publish("my-image-with-numpy:v1")
|
|
2964
2988
|
```
|
|
2965
2989
|
"""
|
|
2966
|
-
tag = _parse_named_image_ref(name)
|
|
2990
|
+
namespace_prefix, tag = _parse_named_image_ref(name)
|
|
2991
|
+
|
|
2992
|
+
if namespace_prefix:
|
|
2993
|
+
if environment_name:
|
|
2994
|
+
raise InvalidError("Cannot specify 'environment_name' when the image name contains a '/'.")
|
|
2995
|
+
resolved_env = ""
|
|
2996
|
+
else:
|
|
2997
|
+
resolved_env = environment_name or config.get("environment") or ""
|
|
2967
2998
|
|
|
2968
2999
|
if self._object_id is None:
|
|
2969
3000
|
raise InvalidError("Cannot publish an image that has not been created yet. Call `.build()` first.")
|
|
2970
3001
|
|
|
2971
3002
|
_client = client or await _Client.from_env()
|
|
2972
3003
|
|
|
2973
|
-
resolved_env = environment_name or config.get("environment") or ""
|
|
2974
|
-
|
|
2975
3004
|
await _client.stub.ImagePublish(
|
|
2976
3005
|
api_pb2.ImagePublishRequest(
|
|
2977
3006
|
image_id=self._object_id,
|
|
@@ -163,7 +163,6 @@ RETRYABLE_GRPC_STATUS_CODES = [
|
|
|
163
163
|
Status.UNKNOWN,
|
|
164
164
|
]
|
|
165
165
|
SERVER_RETRY_WARNING_TIME_INTERVAL = 30.0
|
|
166
|
-
# Initial per-attempt timeout for TCP/TLS handshake in connect_channel.
|
|
167
166
|
DEFAULT_MAX_RETRIES = 3
|
|
168
167
|
|
|
169
168
|
|
|
@@ -308,11 +307,6 @@ def create_channel(
|
|
|
308
307
|
|
|
309
308
|
# With 18 attempts, the max delays between calls is: 0.1 + 0.2 + 0.4 + 0.8 + 1.6 + 3.2 + 5*11 ~ 62
|
|
310
309
|
@retry(n_attempts=18, base_delay=0.1, attempt_timeout=10.0, max_delay=5.0, total_timeout=63.0)
|
|
311
|
-
async def connect_channel(channel: grpclib.client.Channel):
|
|
312
|
-
"""Connect to socket and raise exceptions when there is a connection issue."""
|
|
313
|
-
await channel.__connect__()
|
|
314
|
-
|
|
315
|
-
|
|
316
310
|
async def create_channel_with_fallbacks(
|
|
317
311
|
server_url: str,
|
|
318
312
|
metadata: dict[str, str] = {},
|
|
@@ -323,21 +317,35 @@ async def create_channel_with_fallbacks(
|
|
|
323
317
|
`server_url` may be a single URL or a comma-separated list of URLs. The connection attempts
|
|
324
318
|
are raced against each other and the first one to connect wins, which lets the client fail
|
|
325
319
|
over to a secondary endpoint when the primary is unreachable (e.g. a DNS outage on the
|
|
326
|
-
primary's domain).
|
|
327
|
-
|
|
328
|
-
|
|
320
|
+
primary's domain).
|
|
321
|
+
|
|
322
|
+
Attempts are staggered so earlier URLs (e.g. the primary) get a head start and are preferred
|
|
323
|
+
when reachable: the candidate at index `i` waits up to `i * stagger_delay` seconds before
|
|
324
|
+
starting. That wait is cut short as soon as the previous candidate *fails*, so a fast failure
|
|
325
|
+
falls over to the next URL immediately instead of waiting out the full stagger. Losing attempts
|
|
326
|
+
are cancelled and their channels closed.
|
|
329
327
|
"""
|
|
330
328
|
urls = [url.strip() for url in server_url.split(",")]
|
|
331
329
|
|
|
330
|
+
# Set when a candidate's connection attempt fails, letting the next candidate start immediately
|
|
331
|
+
# instead of waiting out its stagger delay.
|
|
332
|
+
failed = [asyncio.Event() for _ in urls]
|
|
333
|
+
|
|
332
334
|
async def connect(index: int, url: str) -> grpclib.client.Channel:
|
|
333
|
-
|
|
334
|
-
|
|
335
|
+
if index > 0:
|
|
336
|
+
# Stagger the start so earlier URLs are preferred, but start early if the previous
|
|
337
|
+
# candidate has already failed.
|
|
338
|
+
try:
|
|
339
|
+
await asyncio.wait_for(failed[index - 1].wait(), timeout=index * stagger_delay)
|
|
340
|
+
except asyncio.TimeoutError:
|
|
341
|
+
pass
|
|
335
342
|
channel = create_channel(url, metadata)
|
|
336
343
|
try:
|
|
337
|
-
await
|
|
344
|
+
await channel.__connect__()
|
|
338
345
|
except BaseException:
|
|
339
346
|
# Close the channel of a losing/cancelled attempt so its socket doesn't leak.
|
|
340
347
|
channel.close()
|
|
348
|
+
failed[index].set()
|
|
341
349
|
raise
|
|
342
350
|
return channel
|
|
343
351
|
|
|
@@ -29,7 +29,13 @@ def is_valid_object_name(name: str) -> bool:
|
|
|
29
29
|
|
|
30
30
|
def is_valid_environment_name(name: str) -> bool:
|
|
31
31
|
# first char is alnum, the rest allows other chars
|
|
32
|
-
|
|
32
|
+
# The "en-" prefix is reserved for environment IDs, so that references
|
|
33
|
+
# to environments can be resolved by either name or ID unambiguously.
|
|
34
|
+
return (
|
|
35
|
+
len(name) <= 64
|
|
36
|
+
and re.match(r"^[a-zA-Z0-9][a-zA-Z0-9-_.]+$", name) is not None
|
|
37
|
+
and not name.lower().startswith("en-")
|
|
38
|
+
)
|
|
33
39
|
|
|
34
40
|
|
|
35
41
|
def is_valid_tag(tag: str, max_length: int = 50) -> bool:
|
|
@@ -66,7 +72,7 @@ def check_environment_name(name: str) -> None:
|
|
|
66
72
|
f"Invalid Environment name: '{name}'."
|
|
67
73
|
"\n\nEnvironment names can only start with alphanumeric characters,"
|
|
68
74
|
" may contain only alphanumeric characters, dashes, periods, and underscores,"
|
|
69
|
-
"
|
|
75
|
+
" must be shorter than 64 characters, and cannot start with 'en-'."
|
|
70
76
|
)
|
|
71
77
|
if not is_valid_environment_name(name):
|
|
72
78
|
raise InvalidError(message)
|
|
@@ -32,8 +32,8 @@ from .grpc_utils import RETRYABLE_GRPC_STATUS_CODES
|
|
|
32
32
|
async def _connect_channel(channel: grpclib.client.Channel):
|
|
33
33
|
"""Connect to the command router channel.
|
|
34
34
|
|
|
35
|
-
Uses a longer retry budget than grpc_utils.
|
|
36
|
-
may take a long time to start on the worker after scheduling.
|
|
35
|
+
Uses a longer retry budget than grpc_utils.create_channel_with_fallbacks. In rare cases the
|
|
36
|
+
sandbox may take a long time to start on the worker after scheduling.
|
|
37
37
|
|
|
38
38
|
Retries with exponential backoff (1, 2, 4, 8, 10, 10, ...) capped at 10s per delay.
|
|
39
39
|
Total sleep between attempts: 1 + 2 + 4 + 8 + 10*29 = 305s (~5 min).
|
|
@@ -13,13 +13,19 @@ from ._object import _Object
|
|
|
13
13
|
from ._resolver import Resolver
|
|
14
14
|
from ._utils.time_utils import timestamp_to_localized_dt
|
|
15
15
|
from .client import _Client
|
|
16
|
-
from .types import
|
|
16
|
+
from .types import (
|
|
17
|
+
BillingReportItem,
|
|
18
|
+
ProxyTokenInfo,
|
|
19
|
+
TokenData,
|
|
20
|
+
WorkspaceMemberInfo,
|
|
21
|
+
WorkspaceSettings,
|
|
22
|
+
)
|
|
17
23
|
|
|
18
24
|
|
|
19
|
-
def _member_role_from_proto(proto_value: int) ->
|
|
25
|
+
def _member_role_from_proto(proto_value: int) -> str:
|
|
20
26
|
match proto_value:
|
|
21
27
|
case api_pb2.MEMBER_ROLE_USER:
|
|
22
|
-
return "
|
|
28
|
+
return "member"
|
|
23
29
|
case api_pb2.MEMBER_ROLE_MANAGER:
|
|
24
30
|
return "manager"
|
|
25
31
|
case api_pb2.MEMBER_ROLE_OWNER:
|
|
@@ -728,7 +728,6 @@ class _App:
|
|
|
728
728
|
"""
|
|
729
729
|
...
|
|
730
730
|
|
|
731
|
-
def _experimental_server(self, **kwargs): ...
|
|
732
731
|
def server(
|
|
733
732
|
self,
|
|
734
733
|
_warn_parentheses_missing=None,
|
|
@@ -1733,7 +1732,6 @@ class App:
|
|
|
1733
1732
|
"""
|
|
1734
1733
|
...
|
|
1735
1734
|
|
|
1736
|
-
def _experimental_server(self, **kwargs): ...
|
|
1737
1735
|
def server(
|
|
1738
1736
|
self,
|
|
1739
1737
|
_warn_parentheses_missing=None,
|
|
@@ -202,20 +202,20 @@ async def logs(
|
|
|
202
202
|
task_info_resp = await client.stub.TaskGetInfo(api_pb2.TaskGetInfoRequest(task_id=task_id))
|
|
203
203
|
app_id = task_info_resp.app_id
|
|
204
204
|
|
|
205
|
-
if not task_info_resp.info.
|
|
206
|
-
# Unlikely race or Modal backend issue, don't treat as a usage exception
|
|
205
|
+
if not task_info_resp.info.enqueued_at:
|
|
207
206
|
return
|
|
208
|
-
|
|
207
|
+
|
|
208
|
+
container_enqueued_dt = datetime.fromtimestamp(task_info_resp.info.enqueued_at, timezone.utc)
|
|
209
209
|
|
|
210
210
|
now = datetime.now(timezone.utc)
|
|
211
211
|
if all_logs:
|
|
212
|
-
since_dt =
|
|
212
|
+
since_dt = container_enqueued_dt
|
|
213
213
|
if task_info_resp.info.finished_at:
|
|
214
214
|
until_dt = datetime.fromtimestamp(task_info_resp.info.finished_at, timezone.utc)
|
|
215
215
|
else:
|
|
216
216
|
until_dt = now
|
|
217
217
|
else:
|
|
218
|
-
since_dt = _parse_time_arg(since, default=
|
|
218
|
+
since_dt = _parse_time_arg(since, default=container_enqueued_dt)
|
|
219
219
|
if task_info_resp.info.finished_at:
|
|
220
220
|
default_until_dt = datetime.fromtimestamp(task_info_resp.info.finished_at, timezone.utc)
|
|
221
221
|
else:
|
|
@@ -7,7 +7,7 @@ from click import UsageError
|
|
|
7
7
|
from rich.text import Text
|
|
8
8
|
|
|
9
9
|
from modal import environments
|
|
10
|
-
from modal._environments import
|
|
10
|
+
from modal._environments import _Environment
|
|
11
11
|
from modal._utils.async_utils import synchronizer
|
|
12
12
|
from modal._utils.name_utils import check_environment_name
|
|
13
13
|
from modal._utils.time_utils import format_interval
|
|
@@ -158,7 +158,7 @@ def members_list(environment: str, json: bool = False):
|
|
|
158
158
|
"--role", type=click.Choice(["contributor", "viewer"]), required=True, help="Role to assign to the member"
|
|
159
159
|
)
|
|
160
160
|
@service_user_option
|
|
161
|
-
def members_update(environment: str, member: str, role:
|
|
161
|
+
def members_update(environment: str, member: str, role: str, service_user: bool = False):
|
|
162
162
|
env = Environment.from_name(environment)
|
|
163
163
|
if service_user:
|
|
164
164
|
env.members.update(service_users={member: role})
|
|
@@ -168,11 +168,18 @@ async def history(
|
|
|
168
168
|
env: Optional[str] = None,
|
|
169
169
|
json: bool = False,
|
|
170
170
|
):
|
|
171
|
+
explicit_env = env # None when --env is not passed by the user
|
|
171
172
|
env = ensure_env(env)
|
|
172
173
|
try:
|
|
173
|
-
name_tag = _parse_named_image_ref(name)
|
|
174
|
+
namespace_prefix, name_tag = _parse_named_image_ref(name)
|
|
174
175
|
except InvalidError as exc:
|
|
175
176
|
raise click.UsageError(str(exc)) from exc
|
|
177
|
+
|
|
178
|
+
if namespace_prefix:
|
|
179
|
+
if explicit_env is not None:
|
|
180
|
+
raise click.UsageError("Cannot specify '--env' when the image name contains a '/'.")
|
|
181
|
+
env = ""
|
|
182
|
+
|
|
176
183
|
client = await _Client.from_env()
|
|
177
184
|
|
|
178
185
|
first_page = await _fetch_history_page(client, env, name_tag, "")
|
|
@@ -35,7 +35,7 @@ class _Client:
|
|
|
35
35
|
server_url: str,
|
|
36
36
|
client_type: int,
|
|
37
37
|
credentials: typing.Optional[tuple[str, str]],
|
|
38
|
-
version: str = "1.5.2.
|
|
38
|
+
version: str = "1.5.2.dev20",
|
|
39
39
|
):
|
|
40
40
|
"""mdmd:hidden
|
|
41
41
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -205,7 +205,7 @@ class Client:
|
|
|
205
205
|
server_url: str,
|
|
206
206
|
client_type: int,
|
|
207
207
|
credentials: typing.Optional[tuple[str, str]],
|
|
208
|
-
version: str = "1.5.2.
|
|
208
|
+
version: str = "1.5.2.dev20",
|
|
209
209
|
):
|
|
210
210
|
"""mdmd:hidden
|
|
211
211
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -112,9 +112,7 @@ class EnvironmentMembersManager:
|
|
|
112
112
|
...
|
|
113
113
|
|
|
114
114
|
class __list_spec(typing_extensions.Protocol):
|
|
115
|
-
def __call__(
|
|
116
|
-
self, /
|
|
117
|
-
) -> dict[typing.Literal["users", "service_users"], dict[str, typing.Literal["viewer", "contributor"]]]:
|
|
115
|
+
def __call__(self, /) -> dict[typing.Literal["users", "service_users"], dict[str, str]]:
|
|
118
116
|
"""Return the members of a restricted Environment with their roles.
|
|
119
117
|
|
|
120
118
|
**Examples:**
|
|
@@ -130,9 +128,7 @@ class EnvironmentMembersManager:
|
|
|
130
128
|
"""
|
|
131
129
|
...
|
|
132
130
|
|
|
133
|
-
async def aio(
|
|
134
|
-
self, /
|
|
135
|
-
) -> dict[typing.Literal["users", "service_users"], dict[str, typing.Literal["viewer", "contributor"]]]:
|
|
131
|
+
async def aio(self, /) -> dict[typing.Literal["users", "service_users"], dict[str, str]]:
|
|
136
132
|
"""Return the members of a restricted Environment with their roles.
|
|
137
133
|
|
|
138
134
|
**Examples:**
|
|
@@ -155,10 +151,8 @@ class EnvironmentMembersManager:
|
|
|
155
151
|
self,
|
|
156
152
|
/,
|
|
157
153
|
*,
|
|
158
|
-
users: typing.Optional[collections.abc.Mapping[str,
|
|
159
|
-
service_users: typing.Optional[
|
|
160
|
-
collections.abc.Mapping[str, typing.Literal["viewer", "contributor"]]
|
|
161
|
-
] = None,
|
|
154
|
+
users: typing.Optional[collections.abc.Mapping[str, str]] = None,
|
|
155
|
+
service_users: typing.Optional[collections.abc.Mapping[str, str]] = None,
|
|
162
156
|
) -> None:
|
|
163
157
|
"""Add or modify roles for members of a restricted Environment.
|
|
164
158
|
|
|
@@ -181,10 +175,8 @@ class EnvironmentMembersManager:
|
|
|
181
175
|
self,
|
|
182
176
|
/,
|
|
183
177
|
*,
|
|
184
|
-
users: typing.Optional[collections.abc.Mapping[str,
|
|
185
|
-
service_users: typing.Optional[
|
|
186
|
-
collections.abc.Mapping[str, typing.Literal["viewer", "contributor"]]
|
|
187
|
-
] = None,
|
|
178
|
+
users: typing.Optional[collections.abc.Mapping[str, str]] = None,
|
|
179
|
+
service_users: typing.Optional[collections.abc.Mapping[str, str]] = None,
|
|
188
180
|
) -> None:
|
|
189
181
|
"""Add or modify roles for members of a restricted Environment.
|
|
190
182
|
|
|
@@ -5,7 +5,7 @@ import enum
|
|
|
5
5
|
from dataclasses import FrozenInstanceError, dataclass
|
|
6
6
|
from datetime import datetime, timezone
|
|
7
7
|
from decimal import Decimal
|
|
8
|
-
from typing import Any, Iterable,
|
|
8
|
+
from typing import Any, Iterable, Optional
|
|
9
9
|
|
|
10
10
|
from modal_proto import api_pb2
|
|
11
11
|
|
|
@@ -54,9 +54,6 @@ class FileWatchEventType(enum.Enum):
|
|
|
54
54
|
Remove = "Remove"
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
MemberRole = Literal["user", "manager", "owner"]
|
|
58
|
-
|
|
59
|
-
|
|
60
57
|
@dataclass
|
|
61
58
|
class DictInfo:
|
|
62
59
|
"""Information about a Dict object."""
|
|
@@ -206,7 +203,7 @@ class WorkspaceMemberInfo:
|
|
|
206
203
|
name: str
|
|
207
204
|
email: str
|
|
208
205
|
user_id: str
|
|
209
|
-
role:
|
|
206
|
+
role: str
|
|
210
207
|
joined_at: datetime
|
|
211
208
|
last_active_at: Optional[datetime] # None if the member has never been active
|
|
212
209
|
|
|
@@ -72,7 +72,7 @@ class WorkspaceMemberInfo:
|
|
|
72
72
|
name: str
|
|
73
73
|
email: str
|
|
74
74
|
user_id: str
|
|
75
|
-
role:
|
|
75
|
+
role: str
|
|
76
76
|
joined_at: datetime.datetime
|
|
77
77
|
last_active_at: typing.Optional[datetime.datetime]
|
|
78
78
|
|
|
@@ -81,7 +81,7 @@ class WorkspaceMemberInfo:
|
|
|
81
81
|
name: str,
|
|
82
82
|
email: str,
|
|
83
83
|
user_id: str,
|
|
84
|
-
role:
|
|
84
|
+
role: str,
|
|
85
85
|
joined_at: datetime.datetime,
|
|
86
86
|
last_active_at: typing.Optional[datetime.datetime],
|
|
87
87
|
) -> None:
|
|
@@ -483,6 +483,10 @@ class ModalClientBase(abc.ABC):
|
|
|
483
483
|
async def MapStartOrContinue(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.MapStartOrContinueRequest, modal_proto.api_pb2.MapStartOrContinueResponse]') -> None:
|
|
484
484
|
pass
|
|
485
485
|
|
|
486
|
+
@abc.abstractmethod
|
|
487
|
+
async def MountBatchedCheckExistence(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.MountBatchedCheckExistenceRequest, modal_proto.api_pb2.MountBatchedCheckExistenceResponse]') -> None:
|
|
488
|
+
pass
|
|
489
|
+
|
|
486
490
|
@abc.abstractmethod
|
|
487
491
|
async def MountGetOrCreate(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.MountGetOrCreateRequest, modal_proto.api_pb2.MountGetOrCreateResponse]') -> None:
|
|
488
492
|
pass
|
|
@@ -1609,6 +1613,12 @@ class ModalClientBase(abc.ABC):
|
|
|
1609
1613
|
modal_proto.api_pb2.MapStartOrContinueRequest,
|
|
1610
1614
|
modal_proto.api_pb2.MapStartOrContinueResponse,
|
|
1611
1615
|
),
|
|
1616
|
+
'/modal.client.ModalClient/MountBatchedCheckExistence': grpclib.const.Handler(
|
|
1617
|
+
self.MountBatchedCheckExistence,
|
|
1618
|
+
grpclib.const.Cardinality.UNARY_UNARY,
|
|
1619
|
+
modal_proto.api_pb2.MountBatchedCheckExistenceRequest,
|
|
1620
|
+
modal_proto.api_pb2.MountBatchedCheckExistenceResponse,
|
|
1621
|
+
),
|
|
1612
1622
|
'/modal.client.ModalClient/MountGetOrCreate': grpclib.const.Handler(
|
|
1613
1623
|
self.MountGetOrCreate,
|
|
1614
1624
|
grpclib.const.Cardinality.UNARY_UNARY,
|
|
@@ -2953,6 +2963,12 @@ class ModalClientStub:
|
|
|
2953
2963
|
modal_proto.api_pb2.MapStartOrContinueRequest,
|
|
2954
2964
|
modal_proto.api_pb2.MapStartOrContinueResponse,
|
|
2955
2965
|
)
|
|
2966
|
+
self.MountBatchedCheckExistence = grpclib.client.UnaryUnaryMethod(
|
|
2967
|
+
channel,
|
|
2968
|
+
'/modal.client.ModalClient/MountBatchedCheckExistence',
|
|
2969
|
+
modal_proto.api_pb2.MountBatchedCheckExistenceRequest,
|
|
2970
|
+
modal_proto.api_pb2.MountBatchedCheckExistenceResponse,
|
|
2971
|
+
)
|
|
2956
2972
|
self.MountGetOrCreate = grpclib.client.UnaryUnaryMethod(
|
|
2957
2973
|
channel,
|
|
2958
2974
|
'/modal.client.ModalClient/MountGetOrCreate',
|