modal 1.5.4.dev0__tar.gz → 1.5.4.dev1__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 (236) hide show
  1. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/PKG-INFO +1 -1
  2. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_environments.py +6 -2
  3. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_image.py +50 -41
  4. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/environment.py +4 -3
  5. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/image.py +13 -25
  6. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/client.pyi +2 -2
  7. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/environments.pyi +14 -2
  8. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/functions.pyi +6 -6
  9. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/image.pyi +6 -4
  10. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal.egg-info/PKG-INFO +1 -1
  11. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/api_pb2.py +702 -702
  12. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/api_pb2.pyi +16 -3
  13. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_version/__init__.py +1 -1
  14. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/LICENSE +0 -0
  15. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/README.md +0 -0
  16. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/__init__.py +0 -0
  17. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/__main__.py +0 -0
  18. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_billing.py +0 -0
  19. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_clustered_functions.py +0 -0
  20. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_clustered_functions.pyi +0 -0
  21. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_container_entrypoint.py +0 -0
  22. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_function_variants.py +0 -0
  23. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_functions.py +0 -0
  24. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_grpc_client.py +0 -0
  25. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_ipython.py +0 -0
  26. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_load_context.py +0 -0
  27. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_location.py +0 -0
  28. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_logs.py +0 -0
  29. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_logs_manager.py +0 -0
  30. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_logs_manager.pyi +0 -0
  31. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_object.py +0 -0
  32. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_output/__init__.py +0 -0
  33. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_output/manager.py +0 -0
  34. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_output/pty.py +0 -0
  35. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_output/rich.py +0 -0
  36. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_output/status.py +0 -0
  37. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_partial_function.py +0 -0
  38. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_resolver.py +0 -0
  39. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_resources.py +0 -0
  40. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/__init__.py +0 -0
  41. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/asgi.py +0 -0
  42. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/container_io_manager.py +0 -0
  43. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/container_io_manager.pyi +0 -0
  44. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/execution_context.py +0 -0
  45. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/execution_context.pyi +0 -0
  46. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  47. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/task_lifecycle_manager.py +0 -0
  48. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  49. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/telemetry.py +0 -0
  50. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/user_code_event_loop.py +0 -0
  51. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_runtime/user_code_imports.py +0 -0
  52. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_serialization.py +0 -0
  53. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_server.py +0 -0
  54. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_supports_logs.py +0 -0
  55. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_traceback.py +0 -0
  56. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_tunnel.py +0 -0
  57. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_tunnel.pyi +0 -0
  58. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_type_manager.py +0 -0
  59. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/__init__.py +0 -0
  60. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/app_utils.py +0 -0
  61. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/async_utils.py +0 -0
  62. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/auth_token_manager.py +0 -0
  63. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/blob_utils.py +0 -0
  64. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/browser_utils.py +0 -0
  65. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/bytes_io_segment_payload.py +0 -0
  66. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/curl_utils.py +0 -0
  67. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/deprecation.py +0 -0
  68. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/display_utils.py +0 -0
  69. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/docker_utils.py +0 -0
  70. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/function_utils.py +0 -0
  71. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/git_utils.py +0 -0
  72. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/grpc_testing.py +0 -0
  73. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/grpc_utils.py +0 -0
  74. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/hash_utils.py +0 -0
  75. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/http_utils.py +0 -0
  76. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/jwt_utils.py +0 -0
  77. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/logger.py +0 -0
  78. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/mount_utils.py +0 -0
  79. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/name_utils.py +0 -0
  80. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/package_utils.py +0 -0
  81. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/pattern_utils.py +0 -0
  82. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/proxy_support.py +0 -0
  83. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/rand_pb_testing.py +0 -0
  84. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/sandbox_fs_utils.py +0 -0
  85. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/shell_utils.py +0 -0
  86. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/task_command_router_client.py +0 -0
  87. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_utils/time_utils.py +0 -0
  88. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_vendor/__init__.py +0 -0
  89. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  90. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_vendor/cloudpickle.py +0 -0
  91. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_vendor/tblib.py +0 -0
  92. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_vendor/version.py +0 -0
  93. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_watcher.py +0 -0
  94. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/_workspace.py +0 -0
  95. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/app.py +0 -0
  96. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/app.pyi +0 -0
  97. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/billing.py +0 -0
  98. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/billing.pyi +0 -0
  99. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/builder/2023.12.312.txt +0 -0
  100. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/builder/2023.12.txt +0 -0
  101. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/builder/2024.04.txt +0 -0
  102. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/builder/2024.10.txt +0 -0
  103. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/builder/2025.06.txt +0 -0
  104. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/builder/PREVIEW.txt +0 -0
  105. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/builder/README.md +0 -0
  106. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/builder/base-images.json +0 -0
  107. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/call_graph.py +0 -0
  108. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/__init__.py +0 -0
  109. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/_download.py +0 -0
  110. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/_help.py +0 -0
  111. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/_traceback.py +0 -0
  112. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/app.py +0 -0
  113. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/billing.py +0 -0
  114. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/bootstrap.py +0 -0
  115. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/changelog.py +0 -0
  116. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/cluster.py +0 -0
  117. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/config.py +0 -0
  118. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/container.py +0 -0
  119. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/curl.py +0 -0
  120. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/dashboard.py +0 -0
  121. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/dict.py +0 -0
  122. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/endpoint.py +0 -0
  123. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/entry_point.py +0 -0
  124. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/import_refs.py +0 -0
  125. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/launch.py +0 -0
  126. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/logo.py +0 -0
  127. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/network_file_system.py +0 -0
  128. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/profile.py +0 -0
  129. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/programs/__init__.py +0 -0
  130. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/programs/run_jupyter.py +0 -0
  131. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/programs/vscode.py +0 -0
  132. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/queues.py +0 -0
  133. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/run.py +0 -0
  134. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/secret.py +0 -0
  135. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/selector.py +0 -0
  136. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/shell.py +0 -0
  137. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/skills.py +0 -0
  138. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/token.py +0 -0
  139. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/utils.py +0 -0
  140. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/volume.py +0 -0
  141. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cli/workspace.py +0 -0
  142. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/client.py +0 -0
  143. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cloud_bucket_mount.py +0 -0
  144. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cloud_bucket_mount.pyi +0 -0
  145. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cls.py +0 -0
  146. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/cls.pyi +0 -0
  147. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/config.py +0 -0
  148. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/container_process.py +0 -0
  149. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/container_process.pyi +0 -0
  150. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/dict.py +0 -0
  151. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/dict.pyi +0 -0
  152. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/environments.py +0 -0
  153. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/exception.py +0 -0
  154. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/experimental/__init__.py +0 -0
  155. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/experimental/flash.py +0 -0
  156. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/experimental/flash.pyi +0 -0
  157. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/experimental/ipython.py +0 -0
  158. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/file_io.py +0 -0
  159. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/file_io.pyi +0 -0
  160. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/file_pattern_matcher.py +0 -0
  161. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/functions.py +0 -0
  162. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/image.py +0 -0
  163. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/io_streams.py +0 -0
  164. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/io_streams.pyi +0 -0
  165. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/mount.py +0 -0
  166. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/mount.pyi +0 -0
  167. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/network_file_system.py +0 -0
  168. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/network_file_system.pyi +0 -0
  169. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/object.py +0 -0
  170. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/object.pyi +0 -0
  171. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/output.py +0 -0
  172. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/parallel_map.py +0 -0
  173. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/parallel_map.pyi +0 -0
  174. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/partial_function.py +0 -0
  175. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/partial_function.pyi +0 -0
  176. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/proxy.py +0 -0
  177. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/proxy.pyi +0 -0
  178. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/py.typed +0 -0
  179. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/queue.py +0 -0
  180. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/queue.pyi +0 -0
  181. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/retries.py +0 -0
  182. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/runner.py +0 -0
  183. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/runner.pyi +0 -0
  184. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/running_app.py +0 -0
  185. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/sandbox.py +0 -0
  186. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/sandbox.pyi +0 -0
  187. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/sandbox_fs.py +0 -0
  188. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/sandbox_fs.pyi +0 -0
  189. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/schedule.py +0 -0
  190. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/scheduler_placement.py +0 -0
  191. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/secret.py +0 -0
  192. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/secret.pyi +0 -0
  193. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/server.py +0 -0
  194. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/server.pyi +0 -0
  195. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/serving.py +0 -0
  196. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/serving.pyi +0 -0
  197. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/skills/modal/SKILL.md +0 -0
  198. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/snapshot.py +0 -0
  199. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/snapshot.pyi +0 -0
  200. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/stream_type.py +0 -0
  201. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/token_flow.py +0 -0
  202. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/token_flow.pyi +0 -0
  203. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/types.py +0 -0
  204. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/volume.py +0 -0
  205. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/volume.pyi +0 -0
  206. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/workspace.py +0 -0
  207. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal/workspace.pyi +0 -0
  208. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal.egg-info/SOURCES.txt +0 -0
  209. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal.egg-info/dependency_links.txt +0 -0
  210. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal.egg-info/entry_points.txt +0 -0
  211. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal.egg-info/requires.txt +0 -0
  212. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal.egg-info/top_level.txt +0 -0
  213. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_docs/__init__.py +0 -0
  214. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_docs/gen_cli_docs.py +0 -0
  215. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_docs/gen_cli_docs_main.py +0 -0
  216. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_docs/gen_reference_docs.py +0 -0
  217. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_docs/gen_reference_docs_main.py +0 -0
  218. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_docs/mdmd/__init__.py +0 -0
  219. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_docs/mdmd/mdmd.py +0 -0
  220. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_docs/mdmd/signatures.py +0 -0
  221. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_docs/mdmd/types.py +0 -0
  222. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_docs/reference_intro.md +0 -0
  223. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/__init__.py +0 -0
  224. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/api_grpc.py +0 -0
  225. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/api_pb2_grpc.py +0 -0
  226. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/api_pb2_grpc.pyi +0 -0
  227. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/modal_api_grpc.py +0 -0
  228. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/py.typed +0 -0
  229. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/task_command_router_grpc.py +0 -0
  230. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/task_command_router_pb2.py +0 -0
  231. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/task_command_router_pb2.pyi +0 -0
  232. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  233. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  234. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/modal_version/__main__.py +0 -0
  235. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/pyproject.toml +0 -0
  236. {modal-1.5.4.dev0 → modal-1.5.4.dev1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.4.dev0
3
+ Version: 1.5.4.dev1
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ import builtins
4
4
  from collections.abc import Iterable, Mapping
5
5
  from dataclasses import dataclass
6
6
  from datetime import datetime, timezone
7
- from typing import Literal
7
+ from typing import Any, Literal
8
8
 
9
9
  from google.protobuf.empty_pb2 import Empty
10
10
  from google.protobuf.message import Message
@@ -39,6 +39,7 @@ class _EnvironmentManager:
39
39
  name: str, # Name to use for the new Environment
40
40
  *,
41
41
  restricted: bool = False, # If True, enable RBAC restrictions on the Environment
42
+ experimental_options: dict[str, Any] | None = None, # Experimental options for Environment creation
42
43
  client: _Client | None = None, # Optional client with Modal credentials
43
44
  ) -> None:
44
45
  """Create a new Environment.
@@ -51,7 +52,10 @@ class _EnvironmentManager:
51
52
  """
52
53
  check_environment_name(name)
53
54
  client = await _Client.from_env() if client is None else client
54
- await client.stub.EnvironmentCreate(api_pb2.EnvironmentCreateRequest(name=name, is_managed=restricted))
55
+ request = api_pb2.EnvironmentCreateRequest(name=name, is_managed=restricted)
56
+ if (experimental_options or {}).get("is_public", False):
57
+ request.environment_type = api_pb2.ENVIRONMENT_TYPE_PUBLIC
58
+ await client.stub.EnvironmentCreate(request)
55
59
 
56
60
  async def list(
57
61
  self,
@@ -117,34 +117,51 @@ 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) -> 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
- """
131
- image_name, sep, tag = name.partition(":")
120
+ def _parse_image_name(partial_name: str) -> tuple[str | None, str]:
121
+ partial_name_no_env, sep, tag = partial_name.partition(":")
132
122
  if not sep:
133
123
  tag = "latest"
134
124
 
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.")
125
+ if "/" in partial_name_no_env:
126
+ environment, image_name = partial_name_no_env.rsplit("/", 1)
127
+ if not environment:
128
+ raise InvalidError("Invalid Image name: environment prefix before '/' must be non-empty.")
140
129
  if not image_name:
141
- raise InvalidError("Invalid Image name: name after '/' must be non-empty.")
130
+ raise InvalidError("Invalid Image name: name after last '/' must be non-empty.")
131
+ else:
132
+ environment = None
133
+ image_name = partial_name_no_env
142
134
 
143
135
  _validate_image_name(image_name)
144
136
  _validate_image_tag(tag)
145
137
 
146
- full_name = f"{prefix}/{image_name}" if prefix else image_name
147
- return prefix, f"{full_name}:{tag}"
138
+ return environment, f"{image_name}:{tag}"
139
+
140
+
141
+ def _upgrade_image_name(
142
+ partial_name: str,
143
+ *,
144
+ explicit_environment_name: str | None = None,
145
+ fallback_environment_name: str | None = None,
146
+ ) -> str:
147
+ """Ensures an Image name has a tag and an environment name when possible
148
+
149
+ * If no tag is present, add :latest
150
+ * If an explicit environment name is passed, add it as a prefix and error
151
+ if there is already a prefix
152
+ * If a fallback environment name is passed, add it only when there is no prefix
153
+
154
+ """
155
+ prefix_environment, name_tag = _parse_image_name(partial_name)
156
+ if prefix_environment:
157
+ if explicit_environment_name is not None:
158
+ raise InvalidError("environment_name cannot be used when an Image name includes an environment prefix.")
159
+ return f"{prefix_environment}/{name_tag}"
160
+
161
+ environment_name = explicit_environment_name if explicit_environment_name is not None else fallback_environment_name
162
+ if environment_name:
163
+ return f"{environment_name}/{name_tag}"
164
+ return name_tag # no explicit environment - use just the unprefixed name:tag
148
165
 
149
166
 
150
167
  def _validate_python_version(
@@ -2932,8 +2949,8 @@ class _Image(_Object, type_prefix="im"):
2932
2949
  ) -> "_Image":
2933
2950
  """Reference a named Image that was previously published with `.publish()`.
2934
2951
 
2935
- Names can contain an optional `:tag` part - if no tag part is included `":latest"` is used,
2936
- matching Docker conventions.
2952
+ Names can contain an optional `:tag` part. If no tag part is included, `":latest"` is used, matching
2953
+ Docker conventions.
2937
2954
 
2938
2955
  ```python notest
2939
2956
  image = modal.Image.from_name("my-image") # references my-image:latest
@@ -2944,20 +2961,16 @@ class _Image(_Object, type_prefix="im"):
2944
2961
  ...
2945
2962
  ```
2946
2963
  """
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 '/'.")
2964
+ _upgrade_image_name(name, explicit_environment_name=environment_name) # validate
2951
2965
 
2952
2966
  async def _load(self: _Image, resolver: Resolver, load_context: LoadContext, existing_object_id: str | None):
2953
2967
  req = api_pb2.ImageGetByTagRequest(
2954
- tag=tag,
2955
- environment_name="" if namespace_prefix else load_context.environment_name,
2968
+ tag=_upgrade_image_name(name, fallback_environment_name=load_context.environment_name),
2956
2969
  )
2957
2970
  response = await load_context.client.stub.ImageGetByTag(req)
2958
2971
  self._hydrate(response.image_id, load_context.client, None)
2959
2972
 
2960
- rep = _Image._repr(tag, environment_name)
2973
+ rep = _Image._repr(name, environment_name)
2961
2974
  return _Image._from_loader(
2962
2975
  _load,
2963
2976
  rep,
@@ -2971,13 +2984,14 @@ class _Image(_Object, type_prefix="im"):
2971
2984
  name: str,
2972
2985
  *,
2973
2986
  environment_name: str | None = None,
2987
+ experimental_options: dict[str, Any] | None = None,
2974
2988
  client: _Client | None = None,
2975
2989
  ) -> None:
2976
2990
  """Publish this image under the given name
2977
2991
 
2978
2992
  The Image must already be created (typically by calling `image.build()` or `sandbox.snapshot_filesystem()`).
2979
2993
 
2980
- Image names can contain an explicit tag designation (using the `name:tag`). If no tag is included in the name,
2994
+ Image names can contain an explicit tag designation using `name:tag`. If no tag is included in the name,
2981
2995
  `":latest"` is used, matching Docker conventions. To publish multiple tags, call `.publish()` once per tag.
2982
2996
 
2983
2997
  ```python notest
@@ -2987,25 +3001,20 @@ class _Image(_Object, type_prefix="im"):
2987
3001
  image.publish("my-image-with-numpy:v1")
2988
3002
  ```
2989
3003
  """
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 ""
2998
-
2999
3004
  if self._object_id is None:
3000
3005
  raise InvalidError("Cannot publish an image that has not been created yet. Call `.build()` first.")
3001
3006
 
3002
3007
  _client = client or await _Client.from_env()
3008
+ tag = _upgrade_image_name(
3009
+ name,
3010
+ explicit_environment_name=environment_name,
3011
+ fallback_environment_name=config.get("environment"),
3012
+ )
3003
3013
 
3004
3014
  await _client.stub.ImagePublish(
3005
3015
  api_pb2.ImagePublishRequest(
3006
3016
  image_id=self._object_id,
3007
- environment_name=resolved_env,
3008
- allow_public=False,
3017
+ allow_public=bool((experimental_options or {}).get("is_public", False)),
3009
3018
  tag=tag,
3010
3019
  )
3011
3020
  )
@@ -72,10 +72,11 @@ def list_(json: bool = False):
72
72
  @environment_cli.command("create", help="Create a new environment in the current workspace.", no_args_is_help=True)
73
73
  @click.argument("name")
74
74
  @click.option("--restricted", is_flag=True, default=False, help="Enable RBAC restrictions on the new environment")
75
- def create(name: str, restricted: bool = False):
75
+ @click.option("--public", is_flag=True, default=False, hidden=True)
76
+ def create(name: str, restricted: bool = False, public: bool = False):
76
77
  check_environment_name(name)
77
- Environment.objects.create(name, restricted=restricted)
78
- prefix = "Restricted " if restricted else ""
78
+ Environment.objects.create(name, restricted=restricted, experimental_options={"is_public": public})
79
+ prefix = "Public " if public else "Restricted " if restricted else ""
79
80
  rich.print(f"[green]✓[/green] {prefix}Environment created: {name}")
80
81
 
81
82
 
@@ -10,12 +10,12 @@ from typing import Optional
10
10
  import click
11
11
 
12
12
  from modal._environments import ensure_env
13
- from modal._image import _parse_named_image_ref
13
+ from modal._image import _upgrade_image_name
14
14
  from modal._utils.async_utils import synchronizer
15
15
  from modal._utils.time_utils import timestamp_to_localized_str
16
16
  from modal.cli.utils import display_table, env_option
17
17
  from modal.client import _Client
18
- from modal.exception import InvalidError
18
+ from modal.config import config
19
19
  from modal.output import OutputManager
20
20
  from modal_proto import api_pb2
21
21
 
@@ -91,14 +91,12 @@ async def _iter_tag_pages(
91
91
 
92
92
  async def _fetch_history_page(
93
93
  client: _Client,
94
- env: str,
95
- tag: str,
94
+ name_tag: str,
96
95
  page_token: str,
97
96
  ) -> api_pb2.ImageTagRevisionsResponse:
98
97
  return await client.stub.ImageTagRevisions(
99
98
  api_pb2.ImageTagRevisionsRequest(
100
- tag=tag,
101
- environment_name=env,
99
+ tag=name_tag,
102
100
  max_objects=IMAGE_NAMES_HISTORY_PAGE_SIZE,
103
101
  page_token=page_token,
104
102
  )
@@ -106,7 +104,7 @@ async def _fetch_history_page(
106
104
 
107
105
 
108
106
  async def _iter_history_pages(
109
- client: _Client, env: str, tag: str, first_page: api_pb2.ImageTagRevisionsResponse
107
+ client: _Client, name_tag: str, first_page: api_pb2.ImageTagRevisionsResponse
110
108
  ) -> AsyncIterator[api_pb2.ImageTagRevisionsResponse]:
111
109
  response = first_page
112
110
 
@@ -116,7 +114,7 @@ async def _iter_history_pages(
116
114
  if not response.next_page_token:
117
115
  return
118
116
  await asyncio.sleep(IMAGE_NAMES_HISTORY_PAGE_DELAY_SECONDS)
119
- response = await _fetch_history_page(client, env, tag, response.next_page_token)
117
+ response = await _fetch_history_page(client, name_tag, response.next_page_token)
120
118
 
121
119
 
122
120
  @image_names_cli.command("list", help="List named Images.")
@@ -160,36 +158,26 @@ async def list_(
160
158
  "history", help="Show publishing history for a named Image.", hidden=True, no_args_is_help=True
161
159
  )
162
160
  @click.argument("name")
163
- @env_option
164
161
  @click.option("--json", is_flag=True, default=False)
165
162
  @synchronizer.create_blocking
166
163
  async def history(
167
164
  name: str,
168
- env: Optional[str] = None,
169
165
  json: bool = False,
170
166
  ):
171
- explicit_env = env # None when --env is not passed by the user
172
- env = ensure_env(env)
173
- try:
174
- namespace_prefix, name_tag = _parse_named_image_ref(name)
175
- except InvalidError as exc:
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
-
167
+ upgraded_image_name = _upgrade_image_name(
168
+ name,
169
+ fallback_environment_name=config.get("environment"),
170
+ )
183
171
  client = await _Client.from_env()
184
172
 
185
- first_page = await _fetch_history_page(client, env, name_tag, "")
173
+ first_page = await _fetch_history_page(client, upgraded_image_name, "")
186
174
  if not first_page.items:
187
175
  raise click.ClickException(f"No publishing history found for named image {first_page.tag!r}.")
188
176
 
189
177
  if json:
190
178
  click.echo("[", nl=False)
191
179
  count = 0
192
- async for response in _iter_history_pages(client, env, name_tag, first_page):
180
+ async for response in _iter_history_pages(client, upgraded_image_name, first_page):
193
181
  for item in response.items:
194
182
  if count:
195
183
  click.echo(",", nl=False)
@@ -200,7 +188,7 @@ async def history(
200
188
 
201
189
  count = 0
202
190
  show_title = True
203
- async for response in _iter_history_pages(client, env, name_tag, first_page):
191
+ async for response in _iter_history_pages(client, upgraded_image_name, first_page):
204
192
  if response.items:
205
193
  title = f"History for {response.tag}" if show_title else ""
206
194
  display_table(
@@ -37,7 +37,7 @@ class _Client:
37
37
  server_url: str,
38
38
  client_type: int,
39
39
  credentials: typing.Optional[tuple[str, str]],
40
- version: str = "1.5.4.dev0",
40
+ version: str = "1.5.4.dev1",
41
41
  ):
42
42
  """mdmd:hidden
43
43
  The Modal client object is not intended to be instantiated directly by users.
@@ -207,7 +207,7 @@ class Client:
207
207
  server_url: str,
208
208
  client_type: int,
209
209
  credentials: typing.Optional[tuple[str, str]],
210
- version: str = "1.5.4.dev0",
210
+ version: str = "1.5.4.dev1",
211
211
  ):
212
212
  """mdmd:hidden
213
213
  The Modal client object is not intended to be instantiated directly by users.
@@ -18,7 +18,13 @@ class EnvironmentManager:
18
18
 
19
19
  class __create_spec(typing_extensions.Protocol):
20
20
  def __call__(
21
- self, /, name: str, *, restricted: bool = False, client: typing.Optional[modal.client.Client] = None
21
+ self,
22
+ /,
23
+ name: str,
24
+ *,
25
+ restricted: bool = False,
26
+ experimental_options: typing.Optional[dict[str, typing.Any]] = None,
27
+ client: typing.Optional[modal.client.Client] = None,
22
28
  ) -> None:
23
29
  """Create a new Environment.
24
30
 
@@ -31,7 +37,13 @@ class EnvironmentManager:
31
37
  ...
32
38
 
33
39
  async def aio(
34
- self, /, name: str, *, restricted: bool = False, client: typing.Optional[modal.client.Client] = None
40
+ self,
41
+ /,
42
+ name: str,
43
+ *,
44
+ restricted: bool = False,
45
+ experimental_options: typing.Optional[dict[str, typing.Any]] = None,
46
+ client: typing.Optional[modal.client.Client] = None,
35
47
  ) -> None:
36
48
  """Create a new Environment.
37
49
 
@@ -516,7 +516,7 @@ class Function(
516
516
 
517
517
  _call_generator: ___call_generator_spec
518
518
 
519
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
519
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
520
520
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
521
521
  """Calls the function remotely, executing it with the given arguments and returning the execution's result.
522
522
 
@@ -541,7 +541,7 @@ class Function(
541
541
  """
542
542
  ...
543
543
 
544
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType]
544
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P]
545
545
 
546
546
  class __remote_gen_spec(typing_extensions.Protocol):
547
547
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -591,7 +591,7 @@ class Function(
591
591
  """
592
592
  ...
593
593
 
594
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
594
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
595
595
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
596
596
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
597
597
 
@@ -624,7 +624,7 @@ class Function(
624
624
  """
625
625
  ...
626
626
 
627
- _experimental_spawn: ___experimental_spawn_spec[modal._functions.P, modal._functions.ReturnType]
627
+ _experimental_spawn: ___experimental_spawn_spec[modal._functions.ReturnType, modal._functions.P]
628
628
 
629
629
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER]):
630
630
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> None: ...
@@ -632,7 +632,7 @@ class Function(
632
632
 
633
633
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P]
634
634
 
635
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
635
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
636
636
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
637
637
  """Calls the function with the given arguments, without waiting for the results.
638
638
 
@@ -665,7 +665,7 @@ class Function(
665
665
  """
666
666
  ...
667
667
 
668
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType]
668
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P]
669
669
 
670
670
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
671
671
  """Return the inner Python object wrapped by this Modal Function.
@@ -1362,8 +1362,8 @@ class Image(modal.object.Object):
1362
1362
  ) -> Image:
1363
1363
  """Reference a named Image that was previously published with `.publish()`.
1364
1364
 
1365
- Names can contain an optional `:tag` part - if no tag part is included `":latest"` is used,
1366
- matching Docker conventions.
1365
+ Names can contain an optional `:tag` part. If no tag part is included, `":latest"` is used, matching
1366
+ Docker conventions.
1367
1367
 
1368
1368
  ```python notest
1369
1369
  image = modal.Image.from_name("my-image") # references my-image:latest
@@ -1383,13 +1383,14 @@ class Image(modal.object.Object):
1383
1383
  name: str,
1384
1384
  *,
1385
1385
  environment_name: typing.Optional[str] = None,
1386
+ experimental_options: typing.Optional[dict[str, typing.Any]] = None,
1386
1387
  client: typing.Optional[modal.client.Client] = None,
1387
1388
  ) -> None:
1388
1389
  """Publish this image under the given name
1389
1390
 
1390
1391
  The Image must already be created (typically by calling `image.build()` or `sandbox.snapshot_filesystem()`).
1391
1392
 
1392
- Image names can contain an explicit tag designation (using the `name:tag`). If no tag is included in the name,
1393
+ Image names can contain an explicit tag designation using `name:tag`. If no tag is included in the name,
1393
1394
  `":latest"` is used, matching Docker conventions. To publish multiple tags, call `.publish()` once per tag.
1394
1395
 
1395
1396
  ```python notest
@@ -1407,13 +1408,14 @@ class Image(modal.object.Object):
1407
1408
  name: str,
1408
1409
  *,
1409
1410
  environment_name: typing.Optional[str] = None,
1411
+ experimental_options: typing.Optional[dict[str, typing.Any]] = None,
1410
1412
  client: typing.Optional[modal.client.Client] = None,
1411
1413
  ) -> None:
1412
1414
  """Publish this image under the given name
1413
1415
 
1414
1416
  The Image must already be created (typically by calling `image.build()` or `sandbox.snapshot_filesystem()`).
1415
1417
 
1416
- Image names can contain an explicit tag designation (using the `name:tag`). If no tag is included in the name,
1418
+ Image names can contain an explicit tag designation using `name:tag`. If no tag is included in the name,
1417
1419
  `":latest"` is used, matching Docker conventions. To publish multiple tags, call `.publish()` once per tag.
1418
1420
 
1419
1421
  ```python notest
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.4.dev0
3
+ Version: 1.5.4.dev1
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0