modal 1.5.6.dev22__tar.gz → 1.5.6.dev24__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.6.dev22 → modal-1.5.6.dev24}/PKG-INFO +1 -1
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_function_variants.py +157 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_functions.py +68 -2
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_output/manager.py +8 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_output/rich.py +5 -1
- modal-1.5.6.dev24/modal/_output/status.py +92 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_server.py +32 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/config.py +8 -6
- modal-1.5.6.dev24/modal/cli/function.py +865 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/server.py +127 -1
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/utils.py +40 -3
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/volume.py +1 -13
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/client.pyi +2 -2
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/config.py +57 -46
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/functions.pyi +30 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/mount.py +1 -1
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/server.pyi +19 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_version/__init__.py +1 -1
- modal-1.5.6.dev22/modal/_output/status.py +0 -98
- modal-1.5.6.dev22/modal/cli/function.py +0 -432
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/LICENSE +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/README.md +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/__init__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/__main__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_billing.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_clustered_functions.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_clustered_functions.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_container_entrypoint.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_environments.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_grpc_client.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_image.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_ipython.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_load_context.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_location.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_logs.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_logs_manager.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_logs_manager.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_object.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_output/__init__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_output/pty.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_partial_function.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_resolver.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_resources.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/__init__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/asgi.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/execution_context.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/task_lifecycle_manager.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/telemetry.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_serialization.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_supports_logs.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_traceback.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_tunnel.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_tunnel.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_type_manager.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/__init__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/app_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/async_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/blob_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/browser_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/curl_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/deprecation.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/display_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/docker_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/function_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/git_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/hash_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/http_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/idle_countdown.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/logger.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/mount_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/name_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/package_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/proxy_support.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/sandbox_fs_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/shell_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/task_command_router_client.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/time_utils.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_vendor/__init__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_vendor/tblib.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_vendor/version.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_watcher.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_workspace.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/app.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/app.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/billing.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/billing.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/2023.12.txt +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/2024.04.txt +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/2024.10.txt +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/2025.06.txt +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/README.md +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/base-images.json +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/call_graph.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/__init__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/_download.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/_help.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/_logs.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/_stats.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/_traceback.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/app.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/billing.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/changelog.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/cluster.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/container.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/curl.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/dashboard.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/dict.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/endpoint.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/entry_point.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/environment.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/image.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/import_refs.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/logo.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/network_file_system.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/profile.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/queues.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/run.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/secret.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/selector.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/shell.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/skills.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/token.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/workspace.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/client.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cls.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cls.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/container_process.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/container_process.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/dict.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/dict.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/environments.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/environments.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/exception.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/__init__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/_flash_container_entry.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/flash.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/flash.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/flash_server.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/flash_server.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/ipython.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/file_pattern_matcher.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/functions.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/image.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/image.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/io_streams.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/io_streams.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/mount.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/network_file_system.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/network_file_system.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/object.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/object.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/outbound_policy.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/outbound_policy.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/output.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/parallel_map.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/parallel_map.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/partial_function.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/partial_function.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/proxy.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/proxy.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/py.typed +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/queue.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/queue.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/retries.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/runner.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/runner.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/running_app.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/sandbox.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/sandbox.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/sandbox_fs.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/sandbox_fs.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/schedule.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/scheduler_placement.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/secret.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/secret.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/server.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/serving.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/serving.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/skills/modal/SKILL.md +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/snapshot.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/snapshot.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/stream_type.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/token_flow.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/token_flow.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/types.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/volume.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/volume.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/workspace.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/workspace.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/requires.txt +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/top_level.txt +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/__init__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/gen_cli_docs_main.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/gen_reference_docs_main.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/mdmd/types.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/reference_intro.md +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/__init__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/api_grpc.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/api_pb2.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/api_pb2.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/py.typed +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_version/__main__.py +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/pyproject.toml +0 -0
- {modal-1.5.6.dev22 → modal-1.5.6.dev24}/setup.cfg +0 -0
|
@@ -4,12 +4,15 @@ from collections.abc import Collection, Sequence, Sized
|
|
|
4
4
|
from pathlib import PurePosixPath
|
|
5
5
|
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
|
+
import google.protobuf.message
|
|
8
|
+
|
|
7
9
|
from modal.types import CloudBucketMountInfo, FunctionInfo, VolumeMountInfo
|
|
8
10
|
from modal_proto import api_pb2
|
|
9
11
|
|
|
10
12
|
from ._resources import convert_fn_config_to_resources_config
|
|
11
13
|
from ._serialization import (
|
|
12
14
|
apply_defaults,
|
|
15
|
+
deserialize_proto_params,
|
|
13
16
|
serialize,
|
|
14
17
|
serialize_proto_params,
|
|
15
18
|
validate_parameter_values,
|
|
@@ -17,11 +20,14 @@ from ._serialization import (
|
|
|
17
20
|
from ._utils.function_utils import _parse_retries
|
|
18
21
|
from ._utils.mount_utils import validate_volumes, validate_volumes_by_object_id
|
|
19
22
|
from .cloud_bucket_mount import _CloudBucketMount, cloud_bucket_mounts_to_proto
|
|
23
|
+
from .exception import InvalidError
|
|
20
24
|
from .retries import Retries
|
|
21
25
|
from .secret import _Secret
|
|
22
26
|
from .volume import _Volume, _volume_to_mount_proto
|
|
23
27
|
|
|
24
28
|
if TYPE_CHECKING:
|
|
29
|
+
from modal.client import _Client
|
|
30
|
+
|
|
25
31
|
from ._functions import _Function
|
|
26
32
|
from ._load_context import LoadContext
|
|
27
33
|
from ._object import _Object
|
|
@@ -190,6 +196,157 @@ class _FunctionOptions:
|
|
|
190
196
|
)
|
|
191
197
|
|
|
192
198
|
|
|
199
|
+
@dataclasses.dataclass(frozen=True)
|
|
200
|
+
class _FunctionOptionsInfo:
|
|
201
|
+
"""Configuration overrides applied to a Function variant.
|
|
202
|
+
|
|
203
|
+
Fields are named after the parameters of the builder methods that set them: `.with_options()`,
|
|
204
|
+
`.with_concurrency()` (`max_inputs`, `target_inputs`), and `.with_batching()` (`max_batch_size`,
|
|
205
|
+
`wait_ms`). Fields are None when the variant does not override that setting.
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
cpu: float | tuple[float, float] | None = None
|
|
209
|
+
memory: int | tuple[int, int] | None = None
|
|
210
|
+
gpu: str | None = None
|
|
211
|
+
secrets: list[str] | None = None
|
|
212
|
+
volumes: dict[str, VolumeMountInfo] | None = None # mount path -> volume and its mount options
|
|
213
|
+
cloud_bucket_mounts: dict[str, CloudBucketMountInfo] | None = None # mount path -> bucket mount
|
|
214
|
+
retries: Retries | None = None
|
|
215
|
+
max_containers: int | None = None
|
|
216
|
+
buffer_containers: int | None = None
|
|
217
|
+
scaledown_window: int | None = None
|
|
218
|
+
timeout: int | None = None
|
|
219
|
+
region: str | list[str] | None = None
|
|
220
|
+
cloud: str | None = None
|
|
221
|
+
routing_region: str | None = None
|
|
222
|
+
max_inputs: int | None = None
|
|
223
|
+
target_inputs: int | None = None
|
|
224
|
+
max_batch_size: int | None = None
|
|
225
|
+
wait_ms: int | None = None
|
|
226
|
+
|
|
227
|
+
@classmethod
|
|
228
|
+
def _from_proto(cls, proto: api_pb2.FunctionOptions) -> "_FunctionOptionsInfo":
|
|
229
|
+
cpu: float | tuple[float, float] | None = None
|
|
230
|
+
memory: int | tuple[int, int] | None = None
|
|
231
|
+
gpu: str | None = None
|
|
232
|
+
if proto.HasField("resources"):
|
|
233
|
+
resources = proto.resources
|
|
234
|
+
if resources.milli_cpu_max > 0:
|
|
235
|
+
cpu = (resources.milli_cpu / 1000, resources.milli_cpu_max / 1000)
|
|
236
|
+
elif resources.milli_cpu > 0:
|
|
237
|
+
cpu = resources.milli_cpu / 1000
|
|
238
|
+
|
|
239
|
+
if resources.memory_mb_max > 0:
|
|
240
|
+
memory = (resources.memory_mb, resources.memory_mb_max)
|
|
241
|
+
elif resources.memory_mb > 0:
|
|
242
|
+
memory = resources.memory_mb
|
|
243
|
+
|
|
244
|
+
if resources.gpu_config.count > 0:
|
|
245
|
+
gpu_config = resources.gpu_config
|
|
246
|
+
gpu = gpu_config.gpu_type if gpu_config.count == 1 else f"{gpu_config.gpu_type}:{gpu_config.count}"
|
|
247
|
+
|
|
248
|
+
retries: Retries | None = None
|
|
249
|
+
if proto.HasField("retry_policy"):
|
|
250
|
+
retry_policy = proto.retry_policy
|
|
251
|
+
retries = Retries(
|
|
252
|
+
max_retries=retry_policy.retries,
|
|
253
|
+
backoff_coefficient=retry_policy.backoff_coefficient or 2.0,
|
|
254
|
+
initial_delay=retry_policy.initial_delay_ms / 1000,
|
|
255
|
+
max_delay=retry_policy.max_delay_ms / 1000 or 60.0,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
region: str | list[str] | None = None
|
|
259
|
+
if proto.HasField("scheduler_placement") and proto.scheduler_placement.regions:
|
|
260
|
+
region = list(proto.scheduler_placement.regions)
|
|
261
|
+
|
|
262
|
+
return cls(
|
|
263
|
+
cpu=cpu,
|
|
264
|
+
memory=memory,
|
|
265
|
+
gpu=gpu,
|
|
266
|
+
secrets=list(proto.secret_ids) or None,
|
|
267
|
+
volumes={
|
|
268
|
+
vm.mount_path: VolumeMountInfo(
|
|
269
|
+
name=None, # The mount records which Volume, not what it was called.
|
|
270
|
+
volume_id=vm.volume_id,
|
|
271
|
+
read_only=vm.read_only,
|
|
272
|
+
sub_path=vm.sub_path if vm.HasField("sub_path") else None,
|
|
273
|
+
)
|
|
274
|
+
for vm in proto.volume_mounts
|
|
275
|
+
}
|
|
276
|
+
or None,
|
|
277
|
+
cloud_bucket_mounts={
|
|
278
|
+
cbm.mount_path: CloudBucketMountInfo._from_proto(cbm) for cbm in proto.cloud_bucket_mounts
|
|
279
|
+
}
|
|
280
|
+
or None,
|
|
281
|
+
retries=retries,
|
|
282
|
+
max_containers=proto.concurrency_limit if proto.HasField("concurrency_limit") else None,
|
|
283
|
+
buffer_containers=proto.buffer_containers if proto.HasField("buffer_containers") else None,
|
|
284
|
+
scaledown_window=proto.task_idle_timeout_secs if proto.HasField("task_idle_timeout_secs") else None,
|
|
285
|
+
timeout=proto.timeout_secs if proto.HasField("timeout_secs") else None,
|
|
286
|
+
region=region,
|
|
287
|
+
cloud=proto.cloud_provider_str if proto.HasField("cloud_provider_str") else None,
|
|
288
|
+
routing_region=proto.routing_region if proto.HasField("routing_region") else None,
|
|
289
|
+
max_inputs=proto.max_concurrent_inputs if proto.HasField("max_concurrent_inputs") else None,
|
|
290
|
+
target_inputs=proto.target_concurrent_inputs if proto.HasField("target_concurrent_inputs") else None,
|
|
291
|
+
max_batch_size=proto.batch_max_size if proto.HasField("batch_max_size") else None,
|
|
292
|
+
wait_ms=proto.batch_linger_ms if proto.HasField("batch_linger_ms") else None,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
@dataclasses.dataclass(frozen=True)
|
|
297
|
+
class _FunctionVariantInfo:
|
|
298
|
+
"""Information about a variant of a Function, created by parametrization or `.with_options()`."""
|
|
299
|
+
|
|
300
|
+
function_id: str
|
|
301
|
+
parameters: dict[str, str | int | bytes | bool] | None # None when the parameters can't be decoded
|
|
302
|
+
options: _FunctionOptionsInfo | None
|
|
303
|
+
|
|
304
|
+
@classmethod
|
|
305
|
+
def _from_proto(cls, proto: api_pb2.FunctionVariantInfo) -> "_FunctionVariantInfo":
|
|
306
|
+
parameters: dict[str, str | int | bytes | bool] | None
|
|
307
|
+
try:
|
|
308
|
+
parameters = deserialize_proto_params(proto.serialized_params)
|
|
309
|
+
except (google.protobuf.message.DecodeError, InvalidError):
|
|
310
|
+
parameters = None
|
|
311
|
+
|
|
312
|
+
return cls(
|
|
313
|
+
function_id=proto.function_id,
|
|
314
|
+
parameters=parameters,
|
|
315
|
+
options=_FunctionOptionsInfo._from_proto(proto.function_options)
|
|
316
|
+
if proto.HasField("function_options")
|
|
317
|
+
else None,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@dataclasses.dataclass(frozen=True)
|
|
322
|
+
class _FunctionVariantListing:
|
|
323
|
+
"""The variants of a Function, and how the server chose to order them."""
|
|
324
|
+
|
|
325
|
+
variants: list[_FunctionVariantInfo]
|
|
326
|
+
# True when the variants are ordered by how many tasks each is running, busiest first.
|
|
327
|
+
ordered_by_task_count: bool
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
async def _list_function_variants(
|
|
331
|
+
client: "_Client", function_id: str, *, limit: int | None = None
|
|
332
|
+
) -> _FunctionVariantListing:
|
|
333
|
+
variants: list[_FunctionVariantInfo] = []
|
|
334
|
+
ordered_by_task_count = False
|
|
335
|
+
cursor = None
|
|
336
|
+
while True:
|
|
337
|
+
request = api_pb2.FunctionListVariantsRequest(function_id=function_id, cursor=cursor, limit=limit or 0)
|
|
338
|
+
response = await client.stub.FunctionListVariants(request)
|
|
339
|
+
# An ordered listing arrives as a single response, so there is nothing to reconcile across
|
|
340
|
+
# pages: every response of a multi-page listing reports itself unordered.
|
|
341
|
+
ordered_by_task_count = response.ordered_by_task_count
|
|
342
|
+
variants.extend(_FunctionVariantInfo._from_proto(info) for info in response.infos)
|
|
343
|
+
if limit is not None and len(variants) >= limit:
|
|
344
|
+
return _FunctionVariantListing(variants[:limit], ordered_by_task_count)
|
|
345
|
+
if not response.HasField("next_cursor"):
|
|
346
|
+
return _FunctionVariantListing(variants, ordered_by_task_count)
|
|
347
|
+
cursor = response.next_cursor
|
|
348
|
+
|
|
349
|
+
|
|
193
350
|
async def _function_bind_params_cached(
|
|
194
351
|
base_function: "_Function",
|
|
195
352
|
req: api_pb2.FunctionBindParamsRequest,
|
|
@@ -10,7 +10,7 @@ from collections.abc import AsyncGenerator, AsyncIterator, Callable, Collection,
|
|
|
10
10
|
from dataclasses import dataclass
|
|
11
11
|
from datetime import datetime, timedelta, timezone
|
|
12
12
|
from pathlib import PurePosixPath
|
|
13
|
-
from typing import TYPE_CHECKING, Any
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
14
14
|
|
|
15
15
|
import typing_extensions
|
|
16
16
|
from google.protobuf.message import Message
|
|
@@ -945,7 +945,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
945
945
|
self._hydrate(response.function_id, load_context.client, response.handle_metadata)
|
|
946
946
|
|
|
947
947
|
async def _load(self: _Function, resolver: Resolver, load_context: LoadContext, existing_object_id: str | None):
|
|
948
|
-
with FunctionCreationStatus(tag) as function_creation_status:
|
|
948
|
+
with FunctionCreationStatus("Server" if is_server else "Function", tag) as function_creation_status:
|
|
949
949
|
timeout_secs = timeout
|
|
950
950
|
|
|
951
951
|
if app and app._is_interactive_ and not is_builder_function:
|
|
@@ -1388,6 +1388,72 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1388
1388
|
_load_remote, rep, skip_reload=True, hydrate_lazily=True, load_context_overrides=load_context_overrides
|
|
1389
1389
|
)
|
|
1390
1390
|
|
|
1391
|
+
@classmethod
|
|
1392
|
+
def _from_id(
|
|
1393
|
+
cls,
|
|
1394
|
+
function_id: str,
|
|
1395
|
+
*,
|
|
1396
|
+
load_context_overrides: LoadContext,
|
|
1397
|
+
called_from: Literal["Function", "Server"] = "Function",
|
|
1398
|
+
):
|
|
1399
|
+
# internal function lookup implementation that allows lookup of class "service functions"
|
|
1400
|
+
# in addition to non-class functions
|
|
1401
|
+
async def _load_remote(
|
|
1402
|
+
self: _Function, resolver: Resolver, load_context: LoadContext, existing_object_id: str | None
|
|
1403
|
+
):
|
|
1404
|
+
request = api_pb2.FunctionGetByIdRequest(function_id=function_id)
|
|
1405
|
+
try:
|
|
1406
|
+
response = await load_context.client.stub.FunctionGetById(request)
|
|
1407
|
+
except NotFoundError as exc:
|
|
1408
|
+
# refine the error message
|
|
1409
|
+
msg = f"Lookup failed for {called_from} '{function_id}': {exc}."
|
|
1410
|
+
raise NotFoundError(msg) from None
|
|
1411
|
+
if called_from == "Function" and (response.function.is_server or response.function.is_class):
|
|
1412
|
+
if response.function.is_server:
|
|
1413
|
+
raise InvalidError(f"{function_id} is a Server. Use \n ``modal.Server.from_id('{function_id}')``")
|
|
1414
|
+
raise InvalidError(f"{function_id} is a Cls and cannot be loaded with Function.from_id().")
|
|
1415
|
+
if called_from == "Server" and not response.function.is_server:
|
|
1416
|
+
if response.function.is_class:
|
|
1417
|
+
raise InvalidError(f"{function_id} is a Cls and cannot be loaded with Server.from_id().")
|
|
1418
|
+
raise InvalidError(f"{function_id} is a Function. Use \n ``modal.Function.from_id('{function_id}')``")
|
|
1419
|
+
self._hydrate(function_id, load_context.client, response.handle_metadata)
|
|
1420
|
+
self._function_info = FunctionInfo._from_function_proto(response.function)
|
|
1421
|
+
|
|
1422
|
+
rep = f"modal.Function.from_id('{function_id}')"
|
|
1423
|
+
return cls._from_loader(
|
|
1424
|
+
_load_remote, rep, skip_reload=True, hydrate_lazily=True, load_context_overrides=load_context_overrides
|
|
1425
|
+
)
|
|
1426
|
+
|
|
1427
|
+
@classmethod
|
|
1428
|
+
def from_id(
|
|
1429
|
+
cls: type["_Function"],
|
|
1430
|
+
function_id: str,
|
|
1431
|
+
*,
|
|
1432
|
+
client: _Client | None = None,
|
|
1433
|
+
):
|
|
1434
|
+
"""Reference a Function from a deployed or running App by its ID.
|
|
1435
|
+
|
|
1436
|
+
This is a lazy method that defers hydrating the local
|
|
1437
|
+
object with metadata from Modal servers until the first
|
|
1438
|
+
time it is actually used.
|
|
1439
|
+
|
|
1440
|
+
Args:
|
|
1441
|
+
function_id: ID of the function.
|
|
1442
|
+
client: Modal client to use; defaults to `Client.from_env()` when omitted.
|
|
1443
|
+
|
|
1444
|
+
Returns:
|
|
1445
|
+
A lazy `Function` handle.
|
|
1446
|
+
|
|
1447
|
+
Examples:
|
|
1448
|
+
```python
|
|
1449
|
+
f = modal.Function.from_id("fu-123")
|
|
1450
|
+
```
|
|
1451
|
+
"""
|
|
1452
|
+
return cls._from_id(
|
|
1453
|
+
function_id,
|
|
1454
|
+
load_context_overrides=LoadContext(client=client),
|
|
1455
|
+
)
|
|
1456
|
+
|
|
1391
1457
|
@classmethod
|
|
1392
1458
|
def from_name(
|
|
1393
1459
|
cls: type["_Function"],
|
|
@@ -58,6 +58,11 @@ class StatusRow(ABC):
|
|
|
58
58
|
"""Append a warning message associated with this row."""
|
|
59
59
|
...
|
|
60
60
|
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def details(self, message: str) -> None:
|
|
63
|
+
"""Append additional details to the row."""
|
|
64
|
+
...
|
|
65
|
+
|
|
61
66
|
@abstractmethod
|
|
62
67
|
def finish(self, message: str) -> None:
|
|
63
68
|
"""Mark the row as finished with the given message."""
|
|
@@ -73,6 +78,9 @@ class DisabledStatusRow(StatusRow):
|
|
|
73
78
|
def warn(self, warning: "api_pb2.Warning") -> None:
|
|
74
79
|
pass
|
|
75
80
|
|
|
81
|
+
def details(self, message: str) -> None:
|
|
82
|
+
pass
|
|
83
|
+
|
|
76
84
|
def finish(self, message: str) -> None:
|
|
77
85
|
pass
|
|
78
86
|
|
|
@@ -194,6 +194,10 @@ class RichStatusRow(StatusRow):
|
|
|
194
194
|
if self._spinner is not None:
|
|
195
195
|
self._spinner.update(text=message)
|
|
196
196
|
|
|
197
|
+
def details(self, message: str) -> None:
|
|
198
|
+
if self._step_node is not None:
|
|
199
|
+
self._step_node.add(f"{message}")
|
|
200
|
+
|
|
197
201
|
def warn(self, warning: api_pb2.Warning) -> None:
|
|
198
202
|
if self._step_node is not None:
|
|
199
203
|
self._step_node.add(f"[yellow]:warning:[/yellow] {warning.message}")
|
|
@@ -201,7 +205,7 @@ class RichStatusRow(StatusRow):
|
|
|
201
205
|
def finish(self, message: str) -> None:
|
|
202
206
|
if self._step_node is not None and self._spinner is not None:
|
|
203
207
|
self._spinner.update(text=message)
|
|
204
|
-
self._step_node.label = f"
|
|
208
|
+
self._step_node.label = f"[green]✓[/green] {message}"
|
|
205
209
|
|
|
206
210
|
|
|
207
211
|
_LOG_FLUSH_THRESHOLD = 32768 # 32KB — flush batched log output roughly every ~400 lines
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Copyright Modal Labs 2022
|
|
2
|
+
"""Function creation status tracking for Modal CLI.
|
|
3
|
+
|
|
4
|
+
This module contains the FunctionCreationStatus context manager used to
|
|
5
|
+
track and display function creation progress.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from modal_proto import api_pb2
|
|
11
|
+
|
|
12
|
+
from .manager import OutputManager
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _get_annotation_for_web_url(url_info: api_pb2.WebUrlInfo) -> str:
|
|
16
|
+
if url_info.truncated:
|
|
17
|
+
suffix = " [grey70](label truncated)[/grey70]"
|
|
18
|
+
elif url_info.label_stolen:
|
|
19
|
+
suffix = " [grey70](label stolen)[/grey70]"
|
|
20
|
+
else:
|
|
21
|
+
suffix = ""
|
|
22
|
+
return suffix
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class FunctionCreationStatus:
|
|
26
|
+
"""Context manager for tracking and displaying function creation progress."""
|
|
27
|
+
|
|
28
|
+
type: str
|
|
29
|
+
name: str
|
|
30
|
+
response: api_pb2.FunctionCreateResponse | None = None
|
|
31
|
+
|
|
32
|
+
def __init__(self, type: str, name: str):
|
|
33
|
+
self.type = type
|
|
34
|
+
self.name = name
|
|
35
|
+
self._output_mgr = OutputManager.get()
|
|
36
|
+
|
|
37
|
+
def __enter__(self):
|
|
38
|
+
self.status_row = self._output_mgr.add_status_row()
|
|
39
|
+
self.status_row.message(f"Creating {self.type} {self.name}...")
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
def set_response(self, resp: api_pb2.FunctionCreateResponse):
|
|
43
|
+
self.response = resp
|
|
44
|
+
|
|
45
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
46
|
+
if exc_type:
|
|
47
|
+
raise exc_val
|
|
48
|
+
|
|
49
|
+
if not self.response:
|
|
50
|
+
self.status_row.finish(f"Unknown error when creating {self.type} {self.name}")
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
for warning in self.response.server_warnings:
|
|
54
|
+
self.status_row.warn(warning)
|
|
55
|
+
|
|
56
|
+
if web_url := self.response.handle_metadata.web_url:
|
|
57
|
+
url_info = self.response.function.web_url_info
|
|
58
|
+
authenticated = self.response.function.webhook_config.requires_proxy_auth
|
|
59
|
+
auth_suffix = " 🔑" if authenticated else " [yellow](unauthenticated)[/yellow]"
|
|
60
|
+
suffix = _get_annotation_for_web_url(url_info)
|
|
61
|
+
|
|
62
|
+
self.status_row.finish(f"Created {self.type} [green]{self.name}[/green]")
|
|
63
|
+
self.status_row.details(f"[dim underline]{web_url}[/dim underline]{auth_suffix}{suffix}")
|
|
64
|
+
for custom_domain in self.response.function.custom_domain_info:
|
|
65
|
+
self.status_row.details(f"Custom domain: [dim underline]{custom_domain.url}[/dim underline]")
|
|
66
|
+
|
|
67
|
+
elif self.response.function.flash_service_urls:
|
|
68
|
+
# Despite the gRPC API types, Servers only have one URL
|
|
69
|
+
authenticated = self.response.function.is_server and not self.response.function.http_config.unauthenticated
|
|
70
|
+
auth_suffix = " 🔑" if authenticated else " [yellow](unauthenticated)[/yellow]"
|
|
71
|
+
self.status_row.finish(f"Created {self.type} [green]{self.name}[/green]")
|
|
72
|
+
url = self.response.function.flash_service_urls[0]
|
|
73
|
+
self.status_row.details(f"[dim underline]{url}[/dim underline]{auth_suffix}")
|
|
74
|
+
|
|
75
|
+
else:
|
|
76
|
+
self.status_row.finish(f"Created {self.type} [green]{self.name}[/green].")
|
|
77
|
+
if self.response.function.method_definitions_set:
|
|
78
|
+
for method_definition in self.response.function.method_definitions.values():
|
|
79
|
+
if method_definition.web_url:
|
|
80
|
+
url_info = method_definition.web_url_info
|
|
81
|
+
suffix = _get_annotation_for_web_url(url_info)
|
|
82
|
+
authenticated = method_definition.webhook_config.requires_proxy_auth
|
|
83
|
+
auth_suffix = " 🔑" if authenticated else " [yellow](unauthenticated)[/yellow]"
|
|
84
|
+
self.status_row.details(
|
|
85
|
+
f"{method_definition.function_name} -> "
|
|
86
|
+
f"[dim underline]{method_definition.web_url}[/dim underline]{auth_suffix}{suffix}"
|
|
87
|
+
)
|
|
88
|
+
for custom_domain in method_definition.custom_domain_info:
|
|
89
|
+
indent = len(method_definition.function_name) * " "
|
|
90
|
+
self.status_row.details(
|
|
91
|
+
f"{indent} -> [dim underline]{custom_domain.url}[/dim underline]{auth_suffix}"
|
|
92
|
+
)
|
|
@@ -273,6 +273,38 @@ class _Server:
|
|
|
273
273
|
)
|
|
274
274
|
return server
|
|
275
275
|
|
|
276
|
+
@classmethod
|
|
277
|
+
def from_id(
|
|
278
|
+
cls: type["_Server"],
|
|
279
|
+
server_id: str,
|
|
280
|
+
*,
|
|
281
|
+
client: _Client | None = None,
|
|
282
|
+
):
|
|
283
|
+
"""Reference a Server from a deployed or running App by its ID.
|
|
284
|
+
|
|
285
|
+
This is a lazy method that defers hydrating the local
|
|
286
|
+
object with metadata from Modal servers until the first
|
|
287
|
+
time it is actually used.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
server_id: The ID of the server.
|
|
291
|
+
client: Modal client instance for this session.
|
|
292
|
+
|
|
293
|
+
Examples:
|
|
294
|
+
```python notest
|
|
295
|
+
server = modal.Server.from_id("fu-456")
|
|
296
|
+
```
|
|
297
|
+
"""
|
|
298
|
+
load_context_overrides = LoadContext(client=client)
|
|
299
|
+
|
|
300
|
+
server = _Server()
|
|
301
|
+
server._service_function = _Function._from_id(
|
|
302
|
+
server_id,
|
|
303
|
+
load_context_overrides=load_context_overrides,
|
|
304
|
+
called_from="Server",
|
|
305
|
+
)
|
|
306
|
+
return server
|
|
307
|
+
|
|
276
308
|
def _is_local(self) -> bool:
|
|
277
309
|
"""Returns True if this Server has local source code available."""
|
|
278
310
|
return self._user_cls is not None
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Copyright Modal Labs 2022
|
|
2
|
-
import
|
|
2
|
+
from json import dumps
|
|
3
3
|
|
|
4
4
|
import click
|
|
5
5
|
|
|
@@ -21,11 +21,12 @@ config_cli = ModalGroup(
|
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
@config_cli.command("show", help="Show current configuration values
|
|
24
|
+
@config_cli.command("show", help="Show current configuration values.")
|
|
25
25
|
@click.option("--redact/--no-redact", default=True, help="Redact secret credential values.")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
@click.option("--include-internal", is_flag=True, help="Include internal feature flags.", hidden=True)
|
|
27
|
+
@click.option("--json", is_flag=True, default=False)
|
|
28
|
+
def show(redact: bool, include_internal: bool, json: bool):
|
|
29
|
+
config_dict = config.to_dict(include_internal=include_internal)
|
|
29
30
|
if redact and config_dict.get("token_secret"):
|
|
30
31
|
config_dict["token_secret"] = "***"
|
|
31
32
|
if redact and config_dict.get("oauth_refresh_token"):
|
|
@@ -33,7 +34,8 @@ def show(redact: bool):
|
|
|
33
34
|
if redact and config_dict.get("oauth_client_secret"):
|
|
34
35
|
config_dict["oauth_client_secret"] = "***"
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
# Accept --json as a no-op for consistency; we may change the default formatting later
|
|
38
|
+
OutputManager.get().print_json(dumps(config_dict))
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
SET_DEFAULT_ENV_HELP = """Set the default Modal environment for the active profile
|