modal 1.5.6.dev19__tar.gz → 1.5.6.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.
Files changed (240) hide show
  1. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/PKG-INFO +1 -1
  2. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/__init__.py +2 -0
  3. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_functions.py +0 -2
  4. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/user_code_imports.py +0 -1
  5. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/task_command_router_client.py +6 -0
  6. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/app.py +1 -1
  7. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/utils.py +14 -5
  8. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/client.pyi +2 -2
  9. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/functions.pyi +6 -6
  10. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/network_file_system.py +12 -0
  11. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/network_file_system.pyi +1 -0
  12. modal-1.5.6.dev20/modal/outbound_policy.py +167 -0
  13. modal-1.5.6.dev20/modal/outbound_policy.pyi +184 -0
  14. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/sandbox.py +54 -0
  15. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/sandbox.pyi +69 -0
  16. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/types.py +20 -11
  17. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal.egg-info/PKG-INFO +1 -1
  18. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal.egg-info/SOURCES.txt +2 -0
  19. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_docs/gen_reference_docs.py +4 -2
  20. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/api_pb2.py +1046 -1036
  21. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/api_pb2.pyi +28 -35
  22. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_version/__init__.py +1 -1
  23. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/LICENSE +0 -0
  24. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/README.md +0 -0
  25. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/__main__.py +0 -0
  26. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_billing.py +0 -0
  27. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_clustered_functions.py +0 -0
  28. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_clustered_functions.pyi +0 -0
  29. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_container_entrypoint.py +0 -0
  30. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_environments.py +0 -0
  31. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_function_variants.py +0 -0
  32. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_grpc_client.py +0 -0
  33. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_image.py +0 -0
  34. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_ipython.py +0 -0
  35. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_load_context.py +0 -0
  36. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_location.py +0 -0
  37. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_logs.py +0 -0
  38. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_logs_manager.py +0 -0
  39. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_logs_manager.pyi +0 -0
  40. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_object.py +0 -0
  41. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_output/__init__.py +0 -0
  42. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_output/manager.py +0 -0
  43. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_output/pty.py +0 -0
  44. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_output/rich.py +0 -0
  45. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_output/status.py +0 -0
  46. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_partial_function.py +0 -0
  47. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_resolver.py +0 -0
  48. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_resources.py +0 -0
  49. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/__init__.py +0 -0
  50. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/asgi.py +0 -0
  51. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/container_io_manager.py +0 -0
  52. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/container_io_manager.pyi +0 -0
  53. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/execution_context.py +0 -0
  54. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/execution_context.pyi +0 -0
  55. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  56. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/task_lifecycle_manager.py +0 -0
  57. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  58. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/telemetry.py +0 -0
  59. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_runtime/user_code_event_loop.py +0 -0
  60. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_serialization.py +0 -0
  61. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_server.py +0 -0
  62. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_supports_logs.py +0 -0
  63. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_traceback.py +0 -0
  64. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_tunnel.py +0 -0
  65. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_tunnel.pyi +0 -0
  66. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_type_manager.py +0 -0
  67. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/__init__.py +0 -0
  68. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/app_utils.py +0 -0
  69. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/async_utils.py +0 -0
  70. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/auth_token_manager.py +0 -0
  71. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/blob_utils.py +0 -0
  72. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/browser_utils.py +0 -0
  73. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/bytes_io_segment_payload.py +0 -0
  74. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/curl_utils.py +0 -0
  75. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/deprecation.py +0 -0
  76. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/display_utils.py +0 -0
  77. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/docker_utils.py +0 -0
  78. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/function_utils.py +0 -0
  79. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/git_utils.py +0 -0
  80. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/grpc_testing.py +0 -0
  81. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/grpc_utils.py +0 -0
  82. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/hash_utils.py +0 -0
  83. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/http_utils.py +0 -0
  84. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/idle_countdown.py +0 -0
  85. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/jwt_utils.py +0 -0
  86. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/logger.py +0 -0
  87. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/mount_utils.py +0 -0
  88. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/name_utils.py +0 -0
  89. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/package_utils.py +0 -0
  90. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/pattern_utils.py +0 -0
  91. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/proxy_support.py +0 -0
  92. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/rand_pb_testing.py +0 -0
  93. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/sandbox_fs_utils.py +0 -0
  94. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/shell_utils.py +0 -0
  95. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_utils/time_utils.py +0 -0
  96. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_vendor/__init__.py +0 -0
  97. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  98. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_vendor/cloudpickle.py +0 -0
  99. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_vendor/tblib.py +0 -0
  100. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_vendor/version.py +0 -0
  101. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_watcher.py +0 -0
  102. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/_workspace.py +0 -0
  103. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/app.py +0 -0
  104. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/app.pyi +0 -0
  105. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/billing.py +0 -0
  106. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/billing.pyi +0 -0
  107. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/builder/2023.12.312.txt +0 -0
  108. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/builder/2023.12.txt +0 -0
  109. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/builder/2024.04.txt +0 -0
  110. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/builder/2024.10.txt +0 -0
  111. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/builder/2025.06.txt +0 -0
  112. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/builder/PREVIEW.txt +0 -0
  113. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/builder/README.md +0 -0
  114. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/builder/base-images.json +0 -0
  115. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/call_graph.py +0 -0
  116. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/__init__.py +0 -0
  117. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/_download.py +0 -0
  118. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/_help.py +0 -0
  119. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/_logs.py +0 -0
  120. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/_stats.py +0 -0
  121. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/_traceback.py +0 -0
  122. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/billing.py +0 -0
  123. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/changelog.py +0 -0
  124. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/cluster.py +0 -0
  125. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/config.py +0 -0
  126. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/container.py +0 -0
  127. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/curl.py +0 -0
  128. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/dashboard.py +0 -0
  129. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/dict.py +0 -0
  130. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/endpoint.py +0 -0
  131. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/entry_point.py +0 -0
  132. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/environment.py +0 -0
  133. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/function.py +0 -0
  134. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/image.py +0 -0
  135. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/import_refs.py +0 -0
  136. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/launch.py +0 -0
  137. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/logo.py +0 -0
  138. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/network_file_system.py +0 -0
  139. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/profile.py +0 -0
  140. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/programs/__init__.py +0 -0
  141. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/programs/run_jupyter.py +0 -0
  142. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/programs/vscode.py +0 -0
  143. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/queues.py +0 -0
  144. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/run.py +0 -0
  145. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/secret.py +0 -0
  146. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/selector.py +0 -0
  147. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/server.py +0 -0
  148. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/shell.py +0 -0
  149. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/skills.py +0 -0
  150. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/token.py +0 -0
  151. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/volume.py +0 -0
  152. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cli/workspace.py +0 -0
  153. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/client.py +0 -0
  154. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cloud_bucket_mount.py +0 -0
  155. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cloud_bucket_mount.pyi +0 -0
  156. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cls.py +0 -0
  157. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/cls.pyi +0 -0
  158. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/config.py +0 -0
  159. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/container_process.py +0 -0
  160. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/container_process.pyi +0 -0
  161. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/dict.py +0 -0
  162. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/dict.pyi +0 -0
  163. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/environments.py +0 -0
  164. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/environments.pyi +0 -0
  165. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/exception.py +0 -0
  166. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/experimental/__init__.py +0 -0
  167. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/experimental/flash.py +0 -0
  168. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/experimental/flash.pyi +0 -0
  169. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/experimental/ipython.py +0 -0
  170. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/file_pattern_matcher.py +0 -0
  171. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/functions.py +0 -0
  172. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/image.py +0 -0
  173. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/image.pyi +0 -0
  174. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/io_streams.py +0 -0
  175. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/io_streams.pyi +0 -0
  176. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/mount.py +0 -0
  177. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/mount.pyi +0 -0
  178. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/object.py +0 -0
  179. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/object.pyi +0 -0
  180. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/output.py +0 -0
  181. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/parallel_map.py +0 -0
  182. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/parallel_map.pyi +0 -0
  183. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/partial_function.py +0 -0
  184. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/partial_function.pyi +0 -0
  185. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/proxy.py +0 -0
  186. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/proxy.pyi +0 -0
  187. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/py.typed +0 -0
  188. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/queue.py +0 -0
  189. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/queue.pyi +0 -0
  190. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/retries.py +0 -0
  191. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/runner.py +0 -0
  192. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/runner.pyi +0 -0
  193. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/running_app.py +0 -0
  194. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/sandbox_fs.py +0 -0
  195. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/sandbox_fs.pyi +0 -0
  196. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/schedule.py +0 -0
  197. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/scheduler_placement.py +0 -0
  198. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/secret.py +0 -0
  199. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/secret.pyi +0 -0
  200. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/server.py +0 -0
  201. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/server.pyi +0 -0
  202. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/serving.py +0 -0
  203. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/serving.pyi +0 -0
  204. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/skills/modal/SKILL.md +0 -0
  205. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/snapshot.py +0 -0
  206. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/snapshot.pyi +0 -0
  207. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/stream_type.py +0 -0
  208. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/token_flow.py +0 -0
  209. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/token_flow.pyi +0 -0
  210. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/volume.py +0 -0
  211. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/volume.pyi +0 -0
  212. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/workspace.py +0 -0
  213. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal/workspace.pyi +0 -0
  214. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal.egg-info/dependency_links.txt +0 -0
  215. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal.egg-info/entry_points.txt +0 -0
  216. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal.egg-info/requires.txt +0 -0
  217. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal.egg-info/top_level.txt +0 -0
  218. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_docs/__init__.py +0 -0
  219. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_docs/gen_cli_docs.py +0 -0
  220. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_docs/gen_cli_docs_main.py +0 -0
  221. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_docs/gen_reference_docs_main.py +0 -0
  222. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_docs/mdmd/__init__.py +0 -0
  223. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_docs/mdmd/mdmd.py +0 -0
  224. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_docs/mdmd/signatures.py +0 -0
  225. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_docs/mdmd/types.py +0 -0
  226. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_docs/reference_intro.md +0 -0
  227. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/__init__.py +0 -0
  228. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/api_grpc.py +0 -0
  229. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/api_pb2_grpc.py +0 -0
  230. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/api_pb2_grpc.pyi +0 -0
  231. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/modal_api_grpc.py +0 -0
  232. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/py.typed +0 -0
  233. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/task_command_router_grpc.py +0 -0
  234. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/task_command_router_pb2.py +0 -0
  235. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/task_command_router_pb2.pyi +0 -0
  236. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  237. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  238. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/modal_version/__main__.py +0 -0
  239. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/pyproject.toml +0 -0
  240. {modal-1.5.6.dev19 → modal-1.5.6.dev20}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.6.dev19
3
+ Version: 1.5.6.dev20
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -23,6 +23,7 @@ try:
23
23
  from .functions import Function, FunctionCall
24
24
  from .image import Image
25
25
  from .network_file_system import NetworkFileSystem
26
+ from .outbound_policy import OutboundPolicy
26
27
  from .output import enable_output
27
28
  from .partial_function import (
28
29
  asgi_app,
@@ -71,6 +72,7 @@ __all__ = [
71
72
  "FunctionCall",
72
73
  "Image",
73
74
  "NetworkFileSystem",
75
+ "OutboundPolicy",
74
76
  "Period",
75
77
  "Probe",
76
78
  "Proxy",
@@ -1103,8 +1103,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1103
1103
  is_class=function_definition.is_class,
1104
1104
  class_parameter_info=function_definition.class_parameter_info,
1105
1105
  is_method=function_definition.is_method,
1106
- use_function_id=function_definition.use_function_id,
1107
- use_method_name=function_definition.use_method_name,
1108
1106
  method_definitions=function_definition.method_definitions,
1109
1107
  method_definitions_set=function_definition.method_definitions_set,
1110
1108
  experimental_options=experimental_options or {},
@@ -562,7 +562,6 @@ def _get_cls_or_user_cls(
562
562
  f"Internal error: Invalid 'service function' identifier {qual_name}. Please contact Modal support"
563
563
  )
564
564
 
565
- assert not function_def.use_method_name # new "placeholder methods" should not be invoked directly!
566
565
  cls_name = parts[0]
567
566
  cls_or_user_cls = getattr(module, cls_name)
568
567
  return cls_or_user_cls
@@ -1077,6 +1077,12 @@ class TaskCommandRouterClient:
1077
1077
  lambda: self._call_with_auth_retry(self._stub.TaskSetNetworkAccess, request)
1078
1078
  )
1079
1079
 
1080
+ async def set_outbound_policy(self, request: sr_pb2.TaskSetOutboundPolicyRequest):
1081
+ with grpc_error_converter():
1082
+ return await call_with_retries_on_transient_errors(
1083
+ lambda: self._call_with_auth_retry(self._stub.TaskSetOutboundPolicy, request)
1084
+ )
1085
+
1080
1086
  async def reload_volumes(
1081
1087
  self, task_id: str, timeout: float, container_id: str = ""
1082
1088
  ) -> sr_pb2.TaskReloadVolumesResponse:
@@ -726,7 +726,7 @@ async def info(app_identifier: str, *, env: str | None = None, json: bool = Fals
726
726
  if len(hardware) > 1:
727
727
  metadata.append(f" ({', '.join(hardware[1:])})", style=None if no_color else "bright_black")
728
728
  if summary.web_function:
729
- metadata.append(" · Web function")
729
+ metadata.append(" · Web Function")
730
730
  if schedule := _app_function_schedule(summary.schedule):
731
731
  metadata.append(" · ")
732
732
  metadata.append(schedule)
@@ -248,12 +248,21 @@ async def _resolve_function_id(
248
248
  raise
249
249
 
250
250
  print_server_warnings(response.server_warnings)
251
- return response.function_id, response.handle_metadata
252
-
253
- if _is_function_id(function_identifier):
251
+ function_id = response.function_id
252
+ function = response.function
253
+ metadata = response.handle_metadata
254
+ elif _is_function_id(function_identifier):
254
255
  get_by_id_response = await client.stub.FunctionGetById(
255
256
  api_pb2.FunctionGetByIdRequest(function_id=function_identifier)
256
257
  )
257
- return function_identifier, get_by_id_response.handle_metadata
258
+ function_id = function_identifier
259
+ function = get_by_id_response.function
260
+ metadata = get_by_id_response.handle_metadata
261
+ else:
262
+ raise UsageError(usage)
263
+
264
+ if function.is_server != (object_type == "Server"):
265
+ actual_type = "Server" if function.is_server else "Function"
266
+ raise UsageError(f"'{function_identifier}' is a {actual_type}. Use `modal {actual_type.lower()} {command}`.")
258
267
 
259
- raise UsageError(usage)
268
+ return function_id, metadata
@@ -77,7 +77,7 @@ class _Client:
77
77
  server_url: str,
78
78
  client_type: int,
79
79
  credentials: typing.Optional[tuple[str, str]],
80
- version: str = "1.5.6.dev19",
80
+ version: str = "1.5.6.dev20",
81
81
  *,
82
82
  oauth_credentials: typing.Optional[_OAuthCredentials] = None,
83
83
  ):
@@ -277,7 +277,7 @@ class Client:
277
277
  server_url: str,
278
278
  client_type: int,
279
279
  credentials: typing.Optional[tuple[str, str]],
280
- version: str = "1.5.6.dev19",
280
+ version: str = "1.5.6.dev20",
281
281
  *,
282
282
  oauth_credentials: typing.Optional[_OAuthCredentials] = None,
283
283
  ):
@@ -653,7 +653,7 @@ class Function(
653
653
 
654
654
  _call_generator: ___call_generator_spec
655
655
 
656
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
656
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
657
657
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
658
658
  """Calls the function remotely, executing it with the given arguments and returning the execution's result.
659
659
 
@@ -678,7 +678,7 @@ class Function(
678
678
  """
679
679
  ...
680
680
 
681
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType]
681
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P]
682
682
 
683
683
  class __remote_gen_spec(typing_extensions.Protocol):
684
684
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -728,7 +728,7 @@ class Function(
728
728
  """
729
729
  ...
730
730
 
731
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
731
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
732
732
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
733
733
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
734
734
 
@@ -761,7 +761,7 @@ class Function(
761
761
  """
762
762
  ...
763
763
 
764
- _experimental_spawn: ___experimental_spawn_spec[modal._functions.P, modal._functions.ReturnType]
764
+ _experimental_spawn: ___experimental_spawn_spec[modal._functions.ReturnType, modal._functions.P]
765
765
 
766
766
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER]):
767
767
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> None: ...
@@ -769,7 +769,7 @@ class Function(
769
769
 
770
770
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P]
771
771
 
772
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
772
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
773
773
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
774
774
  """Calls the function with the given arguments, without waiting for the results.
775
775
 
@@ -802,7 +802,7 @@ class Function(
802
802
  """
803
803
  ...
804
804
 
805
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType]
805
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P]
806
806
 
807
807
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
808
808
  """Return the inner Python object wrapped by this Modal Function.
@@ -22,6 +22,7 @@ from ._object import (
22
22
  from ._resolver import Resolver
23
23
  from ._utils.async_utils import TaskContext, aclosing, async_map, sync_or_async_iter, synchronize_api
24
24
  from ._utils.blob_utils import LARGE_FILE_LIMIT, blob_iter, blob_upload_file
25
+ from ._utils.deprecation import deprecation_warning
25
26
  from ._utils.hash_utils import get_sha256_hex
26
27
  from ._utils.name_utils import check_object_name
27
28
  from .client import _Client
@@ -33,6 +34,14 @@ NETWORK_FILE_SYSTEM_PUT_FILE_CLIENT_TIMEOUT = (
33
34
  ) # 10 min max for transferring files (does not include upload time to s3)
34
35
 
35
36
 
37
+ def _warn_network_file_system_deprecated() -> None:
38
+ deprecation_warning(
39
+ (2026, 9, 16),
40
+ "`modal.NetworkFileSystem` is deprecated and will be removed in a future release. Use `modal.Volume` instead.",
41
+ show_source=False,
42
+ )
43
+
44
+
36
45
  def network_file_system_mount_protos(
37
46
  validated_network_file_systems: list[tuple[str, "_NetworkFileSystem"]],
38
47
  ) -> list[api_pb2.SharedVolumeMount]:
@@ -119,6 +128,7 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
119
128
  ```
120
129
  """
121
130
  check_object_name(name, "NetworkFileSystem")
131
+ _warn_network_file_system_deprecated()
122
132
 
123
133
  async def _load(
124
134
  self: _NetworkFileSystem, resolver: Resolver, load_context: LoadContext, existing_object_id: str | None
@@ -175,6 +185,7 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
175
185
  assert await nfs.listdir("/") == []
176
186
  ```
177
187
  """
188
+ _warn_network_file_system_deprecated()
178
189
  if client is None:
179
190
  client = await _Client.from_env()
180
191
  request = api_pb2.SharedVolumeGetOrCreateRequest(
@@ -201,6 +212,7 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
201
212
  ) -> str:
202
213
  """mdmd:hidden"""
203
214
  check_object_name(deployment_name, "NetworkFileSystem")
215
+ _warn_network_file_system_deprecated()
204
216
  if client is None:
205
217
  client = await _Client.from_env()
206
218
  request = api_pb2.SharedVolumeGetOrCreateRequest(
@@ -9,6 +9,7 @@ import synchronicity.combined_types
9
9
  import typing
10
10
  import typing_extensions
11
11
 
12
+ def _warn_network_file_system_deprecated() -> None: ...
12
13
  def network_file_system_mount_protos(
13
14
  validated_network_file_systems: list[tuple[str, _NetworkFileSystem]],
14
15
  ) -> list[modal_proto.api_pb2.SharedVolumeMount]:
@@ -0,0 +1,167 @@
1
+ # Copyright Modal Labs 2026
2
+ """Immutable configuration for replacing headers in outbound HTTPS traffic."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import re
7
+ from dataclasses import dataclass
8
+ from typing import Mapping, Sequence
9
+
10
+ from modal_proto import api_pb2
11
+
12
+ from ._utils.async_utils import synchronize_api
13
+ from .exception import InvalidError
14
+ from .secret import _Secret
15
+
16
+ _DOMAIN_RE = re.compile(
17
+ r"^(\*\.)?([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\Z"
18
+ )
19
+ _HEADER_NAME_RE = re.compile(r"^[A-Za-z0-9!#$%&'*+.^_`|~-]+\Z")
20
+
21
+ _MAX_HEADER_REPLACEMENTS = 25
22
+
23
+
24
+ def _header_value_has_invalid_chars(value: str) -> bool:
25
+ return any((ord(c) < 0x20 and c != "\t") or ord(c) == 0x7F for c in value)
26
+
27
+
28
+ def _template_references_key(template: str) -> bool:
29
+ """Whether a header value template references a `$KEY`."""
30
+ i = template.find("$")
31
+ while i != -1 and i + 1 < len(template):
32
+ c = template[i + 1]
33
+ if c == "$":
34
+ i = template.find("$", i + 2)
35
+ continue
36
+ if c.isascii() and (c.isalpha() or c == "_"):
37
+ return True
38
+ i = template.find("$", i + 1)
39
+ return False
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class _HeaderReplacement:
44
+ domain: str
45
+ headers: dict[str, str]
46
+ secret: _Secret | None
47
+
48
+
49
+ class _OutboundPolicy:
50
+ """Immutable configuration for replacing headers in outbound HTTPS requests
51
+ from a Sandbox.
52
+
53
+ Header values support templating with keys in a replacement's secret: a
54
+ `$`-prefixed key name in the secret is replaced with the secret value.
55
+ Literal `$` characters are written `$$`.
56
+
57
+ Secret values never enter the Sandbox: they are resolved and injected into
58
+ matching requests outside the container.
59
+
60
+ Examples:
61
+ ```python
62
+ import modal
63
+
64
+ secret = modal.Secret.from_name("api-token")
65
+
66
+ outbound_policy = (
67
+ modal.OutboundPolicy()
68
+ # Inject a secret-backed Authorization header.
69
+ .with_header_replacement(
70
+ domain="example.com",
71
+ secret=secret,
72
+ headers={"Authorization": "Bearer $API_TOKEN"},
73
+ )
74
+ # Inject a static header into requests to another domain.
75
+ .with_header_replacement(
76
+ domain="modal.com",
77
+ headers={"X-Trace-Token": "trace_abcd"},
78
+ )
79
+ )
80
+
81
+ sb = modal.Sandbox.create(outbound_policy=outbound_policy)
82
+ ```
83
+ """
84
+
85
+ _replacements: tuple[_HeaderReplacement, ...]
86
+
87
+ def __init__(self, _replacements: tuple[_HeaderReplacement, ...] = ()):
88
+ self._replacements = _replacements
89
+
90
+ def with_header_replacement(
91
+ self,
92
+ *,
93
+ domain: str,
94
+ headers: Mapping[str, str],
95
+ secret: _Secret | None = None,
96
+ ) -> _OutboundPolicy:
97
+ """Return a new `OutboundPolicy` with an added header replacement.
98
+
99
+ Args:
100
+ domain: Domain the replacements are scoped to. Supports `*.` wildcard
101
+ prefixes (matching the apex domain and subdomains) and a bare `"*"`.
102
+ headers: Header name -> header value. Values support `$KEY` templates
103
+ referencing keys in the replacement's `secret`.
104
+ secret: Named Secret (e.g. from `Secret.from_name`) whose keys may be
105
+ referenced in the header value templates. Static replacements pass no
106
+ secret.
107
+ """
108
+ replacement = _HeaderReplacement(domain=domain, headers=dict(headers), secret=secret)
109
+ return _OutboundPolicy(self._replacements + (replacement,))
110
+
111
+ def _validate(self) -> None:
112
+ """Check all replacements, raising `InvalidError` on the first violation found."""
113
+ total = 0
114
+ for replacement in self._replacements:
115
+ if replacement.domain != "*" and not _DOMAIN_RE.match(replacement.domain):
116
+ raise InvalidError(f"Invalid domain: {replacement.domain!r}")
117
+ if not replacement.headers:
118
+ raise InvalidError("`headers` must contain at least one header")
119
+ for name, value in replacement.headers.items():
120
+ if not _HEADER_NAME_RE.match(name):
121
+ raise InvalidError(f"Invalid header name: {name!r}")
122
+ if _header_value_has_invalid_chars(value):
123
+ raise InvalidError(f"Header value for {name!r} must not contain control characters (except tab)")
124
+ if replacement.secret is None and _template_references_key(value):
125
+ raise InvalidError(f"Header value for {name!r} references a secret key, but no `secret` was passed")
126
+ if replacement.secret is not None and replacement.secret._is_ephemeral:
127
+ raise InvalidError(
128
+ "Outbound policies only support named secrets (e.g. `Secret.from_name`); "
129
+ "ephemeral secrets (e.g. `Secret.from_dict`) are not yet supported"
130
+ )
131
+ total += len(replacement.headers)
132
+ if total > _MAX_HEADER_REPLACEMENTS:
133
+ raise InvalidError(f"Outbound policy cannot have more than {_MAX_HEADER_REPLACEMENTS} header replacements")
134
+
135
+ def _secrets(self) -> list[_Secret]:
136
+ """Deduplicated list of secrets referenced by the policy's replacements."""
137
+ return list(dict.fromkeys(r.secret for r in self._replacements if r.secret is not None))
138
+
139
+ def _to_proto(self) -> api_pb2.OutboundPolicy:
140
+ """Convert to the wire format. Referenced secrets must be hydrated first."""
141
+ return api_pb2.OutboundPolicy(
142
+ header_replacements=[
143
+ api_pb2.OutboundPolicy.HeaderReplacement(
144
+ domain=replacement.domain,
145
+ secret_id=(replacement.secret.object_id if replacement.secret is not None else ""),
146
+ headers=replacement.headers,
147
+ )
148
+ for replacement in self._replacements
149
+ ]
150
+ )
151
+
152
+
153
+ def _validate_compatible_network_access(
154
+ outbound_policy: _OutboundPolicy | None,
155
+ block_network: bool,
156
+ outbound_domain_allowlist: Sequence[str] | None,
157
+ ) -> None:
158
+ """Reject combinations of network configuration and outbound policy that the server does not support."""
159
+ if outbound_policy is None:
160
+ return
161
+ if block_network:
162
+ raise InvalidError("`outbound_policy` cannot be used when `block_network` is enabled")
163
+ if outbound_domain_allowlist:
164
+ raise InvalidError("`outbound_policy` cannot be used with `outbound_domain_allowlist`")
165
+
166
+
167
+ OutboundPolicy = synchronize_api(_OutboundPolicy)
@@ -0,0 +1,184 @@
1
+ import modal.secret
2
+ import modal_proto.api_pb2
3
+ import typing
4
+
5
+ def _header_value_has_invalid_chars(value: str) -> bool: ...
6
+ def _template_references_key(template: str) -> bool:
7
+ """Whether a header value template references a `$KEY`."""
8
+ ...
9
+
10
+ class _HeaderReplacement:
11
+ """_HeaderReplacement(domain: 'str', headers: 'dict[str, str]', secret: '_Secret | None')"""
12
+
13
+ domain: str
14
+ headers: dict[str, str]
15
+ secret: typing.Optional[modal.secret._Secret]
16
+
17
+ def __init__(self, domain: str, headers: dict[str, str], secret: typing.Optional[modal.secret._Secret]) -> None:
18
+ """Initialize self. See help(type(self)) for accurate signature."""
19
+ ...
20
+
21
+ def __repr__(self):
22
+ """Return repr(self)."""
23
+ ...
24
+
25
+ def __eq__(self, other):
26
+ """Return self==value."""
27
+ ...
28
+
29
+ def __setattr__(self, name, value):
30
+ """Implement setattr(self, name, value)."""
31
+ ...
32
+
33
+ def __delattr__(self, name):
34
+ """Implement delattr(self, name)."""
35
+ ...
36
+
37
+ def __hash__(self):
38
+ """Return hash(self)."""
39
+ ...
40
+
41
+ class _OutboundPolicy:
42
+ """Immutable configuration for replacing headers in outbound HTTPS requests
43
+ from a Sandbox.
44
+
45
+ Header values support templating with keys in a replacement's secret: a
46
+ `$`-prefixed key name in the secret is replaced with the secret value.
47
+ Literal `$` characters are written `$$`.
48
+
49
+ Secret values never enter the Sandbox: they are resolved and injected into
50
+ matching requests outside the container.
51
+
52
+ Examples:
53
+ ```python
54
+ import modal
55
+
56
+ secret = modal.Secret.from_name("api-token")
57
+
58
+ outbound_policy = (
59
+ modal.OutboundPolicy()
60
+ # Inject a secret-backed Authorization header.
61
+ .with_header_replacement(
62
+ domain="example.com",
63
+ secret=secret,
64
+ headers={"Authorization": "Bearer $API_TOKEN"},
65
+ )
66
+ # Inject a static header into requests to another domain.
67
+ .with_header_replacement(
68
+ domain="modal.com",
69
+ headers={"X-Trace-Token": "trace_abcd"},
70
+ )
71
+ )
72
+
73
+ sb = modal.Sandbox.create(outbound_policy=outbound_policy)
74
+ ```
75
+ """
76
+
77
+ _replacements: tuple[_HeaderReplacement, ...]
78
+
79
+ def __init__(self, _replacements: tuple[_HeaderReplacement, ...] = ()):
80
+ """Initialize self. See help(type(self)) for accurate signature."""
81
+ ...
82
+
83
+ def with_header_replacement(
84
+ self, *, domain: str, headers: typing.Mapping[str, str], secret: typing.Optional[modal.secret._Secret] = None
85
+ ) -> _OutboundPolicy:
86
+ """Return a new `OutboundPolicy` with an added header replacement.
87
+
88
+ Args:
89
+ domain: Domain the replacements are scoped to. Supports `*.` wildcard
90
+ prefixes (matching the apex domain and subdomains) and a bare `"*"`.
91
+ headers: Header name -> header value. Values support `$KEY` templates
92
+ referencing keys in the replacement's `secret`.
93
+ secret: Named Secret (e.g. from `Secret.from_name`) whose keys may be
94
+ referenced in the header value templates. Static replacements pass no
95
+ secret.
96
+ """
97
+ ...
98
+
99
+ def _validate(self) -> None:
100
+ """Check all replacements, raising `InvalidError` on the first violation found."""
101
+ ...
102
+
103
+ def _secrets(self) -> list[modal.secret._Secret]:
104
+ """Deduplicated list of secrets referenced by the policy's replacements."""
105
+ ...
106
+
107
+ def _to_proto(self) -> modal_proto.api_pb2.OutboundPolicy:
108
+ """Convert to the wire format. Referenced secrets must be hydrated first."""
109
+ ...
110
+
111
+ def _validate_compatible_network_access(
112
+ outbound_policy: typing.Optional[_OutboundPolicy],
113
+ block_network: bool,
114
+ outbound_domain_allowlist: typing.Optional[typing.Sequence[str]],
115
+ ) -> None:
116
+ """Reject combinations of network configuration and outbound policy that the server does not support."""
117
+ ...
118
+
119
+ class OutboundPolicy:
120
+ """Immutable configuration for replacing headers in outbound HTTPS requests
121
+ from a Sandbox.
122
+
123
+ Header values support templating with keys in a replacement's secret: a
124
+ `$`-prefixed key name in the secret is replaced with the secret value.
125
+ Literal `$` characters are written `$$`.
126
+
127
+ Secret values never enter the Sandbox: they are resolved and injected into
128
+ matching requests outside the container.
129
+
130
+ Examples:
131
+ ```python
132
+ import modal
133
+
134
+ secret = modal.Secret.from_name("api-token")
135
+
136
+ outbound_policy = (
137
+ modal.OutboundPolicy()
138
+ # Inject a secret-backed Authorization header.
139
+ .with_header_replacement(
140
+ domain="example.com",
141
+ secret=secret,
142
+ headers={"Authorization": "Bearer $API_TOKEN"},
143
+ )
144
+ # Inject a static header into requests to another domain.
145
+ .with_header_replacement(
146
+ domain="modal.com",
147
+ headers={"X-Trace-Token": "trace_abcd"},
148
+ )
149
+ )
150
+
151
+ sb = modal.Sandbox.create(outbound_policy=outbound_policy)
152
+ ```
153
+ """
154
+
155
+ _replacements: tuple[_HeaderReplacement, ...]
156
+
157
+ def __init__(self, _replacements: tuple[_HeaderReplacement, ...] = ()): ...
158
+ def with_header_replacement(
159
+ self, *, domain: str, headers: typing.Mapping[str, str], secret: typing.Optional[modal.secret.Secret] = None
160
+ ) -> OutboundPolicy:
161
+ """Return a new `OutboundPolicy` with an added header replacement.
162
+
163
+ Args:
164
+ domain: Domain the replacements are scoped to. Supports `*.` wildcard
165
+ prefixes (matching the apex domain and subdomains) and a bare `"*"`.
166
+ headers: Header name -> header value. Values support `$KEY` templates
167
+ referencing keys in the replacement's `secret`.
168
+ secret: Named Secret (e.g. from `Secret.from_name`) whose keys may be
169
+ referenced in the header value templates. Static replacements pass no
170
+ secret.
171
+ """
172
+ ...
173
+
174
+ def _validate(self) -> None:
175
+ """Check all replacements, raising `InvalidError` on the first violation found."""
176
+ ...
177
+
178
+ def _secrets(self) -> list[modal.secret.Secret]:
179
+ """Deduplicated list of secrets referenced by the policy's replacements."""
180
+ ...
181
+
182
+ def _to_proto(self) -> modal_proto.api_pb2.OutboundPolicy:
183
+ """Convert to the wire format. Referenced secrets must be hydrated first."""
184
+ ...