modAL 1.3.2__tar.gz → 1.3.2.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. {modal-1.3.2 → modal-1.3.2.dev0}/PKG-INFO +1 -1
  2. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_object.py +35 -40
  3. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_output.py +1 -2
  4. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/app.py +0 -34
  5. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/container.py +1 -9
  6. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/entry_point.py +0 -2
  7. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/utils.py +2 -4
  8. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/volume.py +0 -28
  9. {modal-1.3.2 → modal-1.3.2.dev0}/modal/client.pyi +10 -2
  10. {modal-1.3.2 → modal-1.3.2.dev0}/modal/functions.pyi +6 -6
  11. {modal-1.3.2 → modal-1.3.2.dev0}/modal/object.pyi +0 -11
  12. {modal-1.3.2 → modal-1.3.2.dev0}/modal/runner.py +5 -1
  13. {modal-1.3.2 → modal-1.3.2.dev0}/modal/token_flow.py +17 -2
  14. {modal-1.3.2 → modal-1.3.2.dev0}/modal/token_flow.pyi +3 -0
  15. {modal-1.3.2 → modal-1.3.2.dev0}/modal.egg-info/PKG-INFO +1 -1
  16. {modal-1.3.2 → modal-1.3.2.dev0}/modal.egg-info/SOURCES.txt +0 -2
  17. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/api.proto +0 -10
  18. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/api_grpc.py +0 -16
  19. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/api_pb2.py +67 -87
  20. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/api_pb2.pyi +0 -28
  21. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/api_pb2_grpc.py +0 -33
  22. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/api_pb2_grpc.pyi +0 -10
  23. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/modal_api_grpc.py +0 -1
  24. {modal-1.3.2 → modal-1.3.2.dev0}/modal_version/__init__.py +1 -1
  25. modal-1.3.2/modal/_utils/browser_utils.py +0 -28
  26. modal-1.3.2/modal/cli/dashboard.py +0 -30
  27. {modal-1.3.2 → modal-1.3.2.dev0}/LICENSE +0 -0
  28. {modal-1.3.2 → modal-1.3.2.dev0}/README.md +0 -0
  29. {modal-1.3.2 → modal-1.3.2.dev0}/modal/__init__.py +0 -0
  30. {modal-1.3.2 → modal-1.3.2.dev0}/modal/__main__.py +0 -0
  31. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_billing.py +0 -0
  32. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_clustered_functions.py +0 -0
  33. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_clustered_functions.pyi +0 -0
  34. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_container_entrypoint.py +0 -0
  35. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_functions.py +0 -0
  36. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_grpc_client.py +0 -0
  37. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_ipython.py +0 -0
  38. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_load_context.py +0 -0
  39. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_location.py +0 -0
  40. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_partial_function.py +0 -0
  41. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_pty.py +0 -0
  42. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_resolver.py +0 -0
  43. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_resources.py +0 -0
  44. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_runtime/__init__.py +0 -0
  45. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_runtime/asgi.py +0 -0
  46. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_runtime/container_io_manager.py +0 -0
  47. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_runtime/container_io_manager.pyi +0 -0
  48. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_runtime/execution_context.py +0 -0
  49. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_runtime/execution_context.pyi +0 -0
  50. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  51. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_runtime/telemetry.py +0 -0
  52. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_runtime/user_code_event_loop.py +0 -0
  53. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_runtime/user_code_imports.py +0 -0
  54. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_serialization.py +0 -0
  55. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_server.py +0 -0
  56. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_traceback.py +0 -0
  57. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_tunnel.py +0 -0
  58. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_tunnel.pyi +0 -0
  59. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_type_manager.py +0 -0
  60. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/__init__.py +0 -0
  61. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/app_utils.py +0 -0
  62. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/async_utils.py +0 -0
  63. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/auth_token_manager.py +0 -0
  64. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/blob_utils.py +0 -0
  65. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/bytes_io_segment_payload.py +0 -0
  66. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/deprecation.py +0 -0
  67. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/docker_utils.py +0 -0
  68. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/function_utils.py +0 -0
  69. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/git_utils.py +0 -0
  70. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/grpc_testing.py +0 -0
  71. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/grpc_utils.py +0 -0
  72. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/hash_utils.py +0 -0
  73. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/http_utils.py +0 -0
  74. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/jwt_utils.py +0 -0
  75. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/logger.py +0 -0
  76. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/mount_utils.py +0 -0
  77. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/name_utils.py +0 -0
  78. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/package_utils.py +0 -0
  79. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/pattern_utils.py +0 -0
  80. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/rand_pb_testing.py +0 -0
  81. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/shell_utils.py +0 -0
  82. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/task_command_router_client.py +0 -0
  83. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_utils/time_utils.py +0 -0
  84. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_vendor/__init__.py +0 -0
  85. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  86. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_vendor/cloudpickle.py +0 -0
  87. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_vendor/tblib.py +0 -0
  88. {modal-1.3.2 → modal-1.3.2.dev0}/modal/_watcher.py +0 -0
  89. {modal-1.3.2 → modal-1.3.2.dev0}/modal/app.py +0 -0
  90. {modal-1.3.2 → modal-1.3.2.dev0}/modal/app.pyi +0 -0
  91. {modal-1.3.2 → modal-1.3.2.dev0}/modal/billing.py +0 -0
  92. {modal-1.3.2 → modal-1.3.2.dev0}/modal/builder/2023.12.312.txt +0 -0
  93. {modal-1.3.2 → modal-1.3.2.dev0}/modal/builder/2023.12.txt +0 -0
  94. {modal-1.3.2 → modal-1.3.2.dev0}/modal/builder/2024.04.txt +0 -0
  95. {modal-1.3.2 → modal-1.3.2.dev0}/modal/builder/2024.10.txt +0 -0
  96. {modal-1.3.2 → modal-1.3.2.dev0}/modal/builder/2025.06.txt +0 -0
  97. {modal-1.3.2 → modal-1.3.2.dev0}/modal/builder/PREVIEW.txt +0 -0
  98. {modal-1.3.2 → modal-1.3.2.dev0}/modal/builder/README.md +0 -0
  99. {modal-1.3.2 → modal-1.3.2.dev0}/modal/builder/base-images.json +0 -0
  100. {modal-1.3.2 → modal-1.3.2.dev0}/modal/call_graph.py +0 -0
  101. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/__init__.py +0 -0
  102. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/_download.py +0 -0
  103. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/_traceback.py +0 -0
  104. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/cluster.py +0 -0
  105. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/config.py +0 -0
  106. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/dict.py +0 -0
  107. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/environment.py +0 -0
  108. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/import_refs.py +0 -0
  109. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/launch.py +0 -0
  110. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/network_file_system.py +0 -0
  111. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/profile.py +0 -0
  112. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/programs/__init__.py +0 -0
  113. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/programs/run_jupyter.py +0 -0
  114. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/programs/vscode.py +0 -0
  115. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/queues.py +0 -0
  116. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/run.py +0 -0
  117. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/secret.py +0 -0
  118. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/shell.py +0 -0
  119. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cli/token.py +0 -0
  120. {modal-1.3.2 → modal-1.3.2.dev0}/modal/client.py +0 -0
  121. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cloud_bucket_mount.py +0 -0
  122. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cloud_bucket_mount.pyi +0 -0
  123. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cls.py +0 -0
  124. {modal-1.3.2 → modal-1.3.2.dev0}/modal/cls.pyi +0 -0
  125. {modal-1.3.2 → modal-1.3.2.dev0}/modal/config.py +0 -0
  126. {modal-1.3.2 → modal-1.3.2.dev0}/modal/container_process.py +0 -0
  127. {modal-1.3.2 → modal-1.3.2.dev0}/modal/container_process.pyi +0 -0
  128. {modal-1.3.2 → modal-1.3.2.dev0}/modal/dict.py +0 -0
  129. {modal-1.3.2 → modal-1.3.2.dev0}/modal/dict.pyi +0 -0
  130. {modal-1.3.2 → modal-1.3.2.dev0}/modal/environments.py +0 -0
  131. {modal-1.3.2 → modal-1.3.2.dev0}/modal/environments.pyi +0 -0
  132. {modal-1.3.2 → modal-1.3.2.dev0}/modal/exception.py +0 -0
  133. {modal-1.3.2 → modal-1.3.2.dev0}/modal/experimental/__init__.py +0 -0
  134. {modal-1.3.2 → modal-1.3.2.dev0}/modal/experimental/flash.py +0 -0
  135. {modal-1.3.2 → modal-1.3.2.dev0}/modal/experimental/flash.pyi +0 -0
  136. {modal-1.3.2 → modal-1.3.2.dev0}/modal/experimental/ipython.py +0 -0
  137. {modal-1.3.2 → modal-1.3.2.dev0}/modal/file_io.py +0 -0
  138. {modal-1.3.2 → modal-1.3.2.dev0}/modal/file_io.pyi +0 -0
  139. {modal-1.3.2 → modal-1.3.2.dev0}/modal/file_pattern_matcher.py +0 -0
  140. {modal-1.3.2 → modal-1.3.2.dev0}/modal/functions.py +0 -0
  141. {modal-1.3.2 → modal-1.3.2.dev0}/modal/gpu.py +0 -0
  142. {modal-1.3.2 → modal-1.3.2.dev0}/modal/image.py +0 -0
  143. {modal-1.3.2 → modal-1.3.2.dev0}/modal/image.pyi +0 -0
  144. {modal-1.3.2 → modal-1.3.2.dev0}/modal/io_streams.py +0 -0
  145. {modal-1.3.2 → modal-1.3.2.dev0}/modal/io_streams.pyi +0 -0
  146. {modal-1.3.2 → modal-1.3.2.dev0}/modal/mount.py +0 -0
  147. {modal-1.3.2 → modal-1.3.2.dev0}/modal/mount.pyi +0 -0
  148. {modal-1.3.2 → modal-1.3.2.dev0}/modal/network_file_system.py +0 -0
  149. {modal-1.3.2 → modal-1.3.2.dev0}/modal/network_file_system.pyi +0 -0
  150. {modal-1.3.2 → modal-1.3.2.dev0}/modal/object.py +0 -0
  151. {modal-1.3.2 → modal-1.3.2.dev0}/modal/output.py +0 -0
  152. {modal-1.3.2 → modal-1.3.2.dev0}/modal/parallel_map.py +0 -0
  153. {modal-1.3.2 → modal-1.3.2.dev0}/modal/parallel_map.pyi +0 -0
  154. {modal-1.3.2 → modal-1.3.2.dev0}/modal/partial_function.py +0 -0
  155. {modal-1.3.2 → modal-1.3.2.dev0}/modal/partial_function.pyi +0 -0
  156. {modal-1.3.2 → modal-1.3.2.dev0}/modal/proxy.py +0 -0
  157. {modal-1.3.2 → modal-1.3.2.dev0}/modal/proxy.pyi +0 -0
  158. {modal-1.3.2 → modal-1.3.2.dev0}/modal/py.typed +0 -0
  159. {modal-1.3.2 → modal-1.3.2.dev0}/modal/queue.py +0 -0
  160. {modal-1.3.2 → modal-1.3.2.dev0}/modal/queue.pyi +0 -0
  161. {modal-1.3.2 → modal-1.3.2.dev0}/modal/retries.py +0 -0
  162. {modal-1.3.2 → modal-1.3.2.dev0}/modal/runner.pyi +0 -0
  163. {modal-1.3.2 → modal-1.3.2.dev0}/modal/running_app.py +0 -0
  164. {modal-1.3.2 → modal-1.3.2.dev0}/modal/sandbox.py +0 -0
  165. {modal-1.3.2 → modal-1.3.2.dev0}/modal/sandbox.pyi +0 -0
  166. {modal-1.3.2 → modal-1.3.2.dev0}/modal/schedule.py +0 -0
  167. {modal-1.3.2 → modal-1.3.2.dev0}/modal/scheduler_placement.py +0 -0
  168. {modal-1.3.2 → modal-1.3.2.dev0}/modal/secret.py +0 -0
  169. {modal-1.3.2 → modal-1.3.2.dev0}/modal/secret.pyi +0 -0
  170. {modal-1.3.2 → modal-1.3.2.dev0}/modal/server.py +0 -0
  171. {modal-1.3.2 → modal-1.3.2.dev0}/modal/server.pyi +0 -0
  172. {modal-1.3.2 → modal-1.3.2.dev0}/modal/serving.py +0 -0
  173. {modal-1.3.2 → modal-1.3.2.dev0}/modal/serving.pyi +0 -0
  174. {modal-1.3.2 → modal-1.3.2.dev0}/modal/snapshot.py +0 -0
  175. {modal-1.3.2 → modal-1.3.2.dev0}/modal/snapshot.pyi +0 -0
  176. {modal-1.3.2 → modal-1.3.2.dev0}/modal/stream_type.py +0 -0
  177. {modal-1.3.2 → modal-1.3.2.dev0}/modal/volume.py +0 -0
  178. {modal-1.3.2 → modal-1.3.2.dev0}/modal/volume.pyi +0 -0
  179. {modal-1.3.2 → modal-1.3.2.dev0}/modal.egg-info/dependency_links.txt +0 -0
  180. {modal-1.3.2 → modal-1.3.2.dev0}/modal.egg-info/entry_points.txt +0 -0
  181. {modal-1.3.2 → modal-1.3.2.dev0}/modal.egg-info/requires.txt +0 -0
  182. {modal-1.3.2 → modal-1.3.2.dev0}/modal.egg-info/top_level.txt +0 -0
  183. {modal-1.3.2 → modal-1.3.2.dev0}/modal_docs/__init__.py +0 -0
  184. {modal-1.3.2 → modal-1.3.2.dev0}/modal_docs/gen_cli_docs.py +0 -0
  185. {modal-1.3.2 → modal-1.3.2.dev0}/modal_docs/gen_reference_docs.py +0 -0
  186. {modal-1.3.2 → modal-1.3.2.dev0}/modal_docs/mdmd/__init__.py +0 -0
  187. {modal-1.3.2 → modal-1.3.2.dev0}/modal_docs/mdmd/mdmd.py +0 -0
  188. {modal-1.3.2 → modal-1.3.2.dev0}/modal_docs/mdmd/signatures.py +0 -0
  189. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/__init__.py +0 -0
  190. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/py.typed +0 -0
  191. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/task_command_router.proto +0 -0
  192. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/task_command_router_grpc.py +0 -0
  193. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/task_command_router_pb2.py +0 -0
  194. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/task_command_router_pb2.pyi +0 -0
  195. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  196. {modal-1.3.2 → modal-1.3.2.dev0}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  197. {modal-1.3.2 → modal-1.3.2.dev0}/modal_version/__main__.py +0 -0
  198. {modal-1.3.2 → modal-1.3.2.dev0}/pyproject.toml +0 -0
  199. {modal-1.3.2 → modal-1.3.2.dev0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.3.2
3
+ Version: 1.3.2.dev0
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -39,41 +39,6 @@ def _get_environment_name(
39
39
  return config.get("environment")
40
40
 
41
41
 
42
- def live_method(method):
43
- @wraps(method)
44
- async def wrapped(self, *args, **kwargs):
45
- await self.hydrate()
46
- return await method(self, *args, **kwargs)
47
-
48
- return wrapped
49
-
50
-
51
- def live_method_gen(method):
52
- @wraps(method)
53
- async def wrapped(self, *args, **kwargs):
54
- await self.hydrate()
55
- async with aclosing(method(self, *args, **kwargs)) as stream:
56
- async for item in stream:
57
- yield item
58
-
59
- return wrapped
60
-
61
-
62
- def live_method_contextmanager(method):
63
- # make sure a wrapped function returning an async context manager
64
- # will not require both an `await func.aio()` and `async with`
65
- # which would have been the case if it was wrapped in live_method
66
-
67
- @wraps(method)
68
- @contextlib.asynccontextmanager
69
- async def wrapped(self, *args, **kwargs):
70
- await self.hydrate()
71
- async with method(self, *args, **kwargs) as ctx:
72
- yield ctx
73
-
74
- return wrapped
75
-
76
-
77
42
  class _Object:
78
43
  _type_prefix: ClassVar[Optional[str]] = None
79
44
  _prefix_to_type: ClassVar[dict[str, type]] = {}
@@ -310,11 +275,6 @@ class _Object:
310
275
  raise AttributeError(f"Attempting to get object_id of unhydrated {self}")
311
276
  return self._object_id
312
277
 
313
- @live_method
314
- async def get_dashboard_url(self) -> str:
315
- """mdmd:hidden"""
316
- return f"https://modal.com/id/{self.object_id}"
317
-
318
278
  @property
319
279
  def client(self) -> _Client:
320
280
  """mdmd:hidden"""
@@ -376,3 +336,38 @@ class _Object:
376
336
  with suppress_tb_frame(): # skip this frame by default
377
337
  await resolver.load(self, root_load_context)
378
338
  return self
339
+
340
+
341
+ def live_method(method):
342
+ @wraps(method)
343
+ async def wrapped(self, *args, **kwargs):
344
+ await self.hydrate()
345
+ return await method(self, *args, **kwargs)
346
+
347
+ return wrapped
348
+
349
+
350
+ def live_method_gen(method):
351
+ @wraps(method)
352
+ async def wrapped(self, *args, **kwargs):
353
+ await self.hydrate()
354
+ async with aclosing(method(self, *args, **kwargs)) as stream:
355
+ async for item in stream:
356
+ yield item
357
+
358
+ return wrapped
359
+
360
+
361
+ def live_method_contextmanager(method):
362
+ # make sure a wrapped function returning an async context manager
363
+ # will not require both an `await func.aio()` and `async with`
364
+ # which would have been the case if it was wrapped in live_method
365
+
366
+ @wraps(method)
367
+ @contextlib.asynccontextmanager
368
+ async def wrapped(self, *args, **kwargs):
369
+ await self.hydrate()
370
+ async with method(self, *args, **kwargs) as ctx:
371
+ yield ctx
372
+
373
+ return wrapped
@@ -545,7 +545,7 @@ async def get_app_logs_loop(
545
545
  output_mgr: OutputManager,
546
546
  app_id: str | None = None,
547
547
  task_id: str | None = None,
548
- sandbox_id: str | None = None,
548
+ app_logs_url: str | None = None,
549
549
  ):
550
550
  last_log_batch_entry_id = ""
551
551
 
@@ -600,7 +600,6 @@ async def get_app_logs_loop(
600
600
  request = api_pb2.AppGetLogsRequest(
601
601
  app_id=app_id or "",
602
602
  task_id=task_id or "",
603
- sandbox_id=sandbox_id or "",
604
603
  timeout=55,
605
604
  last_entry_id=last_log_batch_entry_id,
606
605
  )
@@ -10,9 +10,7 @@ from rich.text import Text
10
10
  from typer import Argument
11
11
 
12
12
  from modal._object import _get_environment_name
13
- from modal._output import make_console
14
13
  from modal._utils.async_utils import synchronizer
15
- from modal._utils.browser_utils import open_url_and_display
16
14
  from modal.client import _Client
17
15
  from modal.environments import ensure_env
18
16
  from modal_proto import api_pb2
@@ -250,35 +248,3 @@ async def history(
250
248
 
251
249
  if deployments_with_dirty_commit and not json:
252
250
  rich.print("* - repo had uncommitted changes")
253
-
254
-
255
- @app_cli.command("dashboard", no_args_is_help=True)
256
- @synchronizer.create_blocking
257
- async def dashboard(
258
- app_identifier: str = APP_IDENTIFIER,
259
- *,
260
- env: Optional[str] = ENV_OPTION,
261
- ):
262
- """Open an App's dashboard page in your web browser.
263
-
264
- **Examples:**
265
-
266
- Open dashboard for an app by name:
267
-
268
- ```
269
- modal app dashboard my-app
270
- ```
271
-
272
- Use a specified environment:
273
-
274
- ```
275
- modal app dashboard my-app --env dev
276
- ```
277
- """
278
- client = await _Client.from_env()
279
- app_id = await get_app_id.aio(app_identifier, env, client)
280
-
281
- console = make_console()
282
- url = f"https://modal.com/id/{app_id}"
283
-
284
- open_url_and_display(url, "App dashboard", console)
@@ -13,7 +13,6 @@ from modal.client import _Client
13
13
  from modal.config import config
14
14
  from modal.container_process import _ContainerProcess
15
15
  from modal.environments import ensure_env
16
- from modal.exception import InvalidError
17
16
  from modal.stream_type import StreamType
18
17
  from modal_proto import api_pb2
19
18
 
@@ -53,14 +52,7 @@ def logs(
53
52
  timestamps: bool = typer.Option(False, "--timestamps", help="Show timestamps for each log line"),
54
53
  ):
55
54
  """Show logs for a specific container, streaming while active."""
56
- task_id, sandbox_id = None, None
57
- if container_id.startswith("sb-"):
58
- sandbox_id = container_id
59
- elif container_id.startswith("ta-"):
60
- task_id = container_id
61
- else:
62
- raise InvalidError(f"Invalid container ID: {container_id}")
63
- stream_app_logs(task_id=task_id, sandbox_id=sandbox_id, show_timestamps=timestamps)
55
+ stream_app_logs(task_id=container_id, show_timestamps=timestamps)
64
56
 
65
57
 
66
58
  @container_cli.command("exec")
@@ -13,7 +13,6 @@ from .app import app_cli
13
13
  from .cluster import cluster_cli
14
14
  from .config import config_cli
15
15
  from .container import container_cli
16
- from .dashboard import dashboard
17
16
  from .dict import dict_cli
18
17
  from .environment import environment_cli
19
18
  from .launch import launch_cli
@@ -119,7 +118,6 @@ async def setup(profile: Optional[str] = None):
119
118
  entrypoint_cli_typer.command("deploy", no_args_is_help=True)(run.deploy)
120
119
  entrypoint_cli_typer.command("serve", no_args_is_help=True)(run.serve)
121
120
  entrypoint_cli_typer.command("shell")(shell_module.shell)
122
- entrypoint_cli_typer.command("dashboard")(dashboard)
123
121
  entrypoint_cli_typer.add_typer(launch_cli)
124
122
 
125
123
  # Deployments
@@ -21,16 +21,14 @@ from ..exception import NotFoundError
21
21
  async def stream_app_logs(
22
22
  app_id: Optional[str] = None,
23
23
  task_id: Optional[str] = None,
24
- sandbox_id: Optional[str] = None,
25
24
  app_logs_url: Optional[str] = None,
26
25
  show_timestamps: bool = False,
27
26
  ):
28
27
  client = await _Client.from_env()
29
- display_id = app_id or task_id or sandbox_id
30
- output_mgr = OutputManager(status_spinner_text=f"Tailing logs for {display_id}", show_timestamps=show_timestamps)
28
+ output_mgr = OutputManager(status_spinner_text=f"Tailing logs for {app_id}", show_timestamps=show_timestamps)
31
29
  try:
32
30
  with output_mgr.show_status_spinner():
33
- await get_app_logs_loop(client, output_mgr, app_id=app_id, task_id=task_id, sandbox_id=sandbox_id)
31
+ await get_app_logs_loop(client, output_mgr, app_id=app_id, task_id=task_id, app_logs_url=app_logs_url)
34
32
  except asyncio.CancelledError:
35
33
  pass
36
34
  except KeyboardInterrupt:
@@ -12,7 +12,6 @@ from typer import Argument, Option, Typer
12
12
  import modal
13
13
  from modal._output import OutputManager, ProgressHandler, make_console
14
14
  from modal._utils.async_utils import synchronizer
15
- from modal._utils.browser_utils import open_url_and_display
16
15
  from modal._utils.time_utils import timestamp_to_localized_str
17
16
  from modal.cli._download import _volume_download
18
17
  from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table
@@ -311,30 +310,3 @@ async def rename(
311
310
  )
312
311
 
313
312
  await _Volume.rename(old_name, new_name, environment_name=env)
314
-
315
-
316
- @volume_cli.command(
317
- name="dashboard",
318
- help="Open the Volume's dashboard page in your web browser.",
319
- rich_help_panel="Management",
320
- )
321
- @synchronizer.create_blocking
322
- async def dashboard(
323
- volume_name: str = Argument(help="Name of the Volume to view in the dashboard."),
324
- env: Optional[str] = ENV_OPTION,
325
- ):
326
- """Open a Volume's dashboard page in your web browser.
327
-
328
- **Example:**
329
-
330
- ```
331
- modal volume dashboard my-volume
332
- ```
333
- """
334
- env = ensure_env(env)
335
- volume = await _Volume.from_name(volume_name, environment_name=env).hydrate()
336
-
337
- console = make_console()
338
- url = f"https://modal.com/id/{volume.object_id}"
339
-
340
- open_url_and_display(url, "Volume dashboard", console)
@@ -31,7 +31,11 @@ class _Client:
31
31
  client_type: int
32
32
 
33
33
  def __init__(
34
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "1.3.2"
34
+ self,
35
+ server_url: str,
36
+ client_type: int,
37
+ credentials: typing.Optional[tuple[str, str]],
38
+ version: str = "1.3.2.dev0",
35
39
  ):
36
40
  """mdmd:hidden
37
41
  The Modal client object is not intended to be instantiated directly by users.
@@ -163,7 +167,11 @@ class Client:
163
167
  client_type: int
164
168
 
165
169
  def __init__(
166
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "1.3.2"
170
+ self,
171
+ server_url: str,
172
+ client_type: int,
173
+ credentials: typing.Optional[tuple[str, str]],
174
+ version: str = "1.3.2.dev0",
167
175
  ):
168
176
  """mdmd:hidden
169
177
  The Modal client object is not intended to be instantiated directly by users.
@@ -408,7 +408,7 @@ class Function(
408
408
 
409
409
  _call_generator: ___call_generator_spec
410
410
 
411
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
411
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
412
412
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
413
413
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
414
414
  ...
@@ -417,7 +417,7 @@ class Function(
417
417
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
418
418
  ...
419
419
 
420
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType]
420
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P]
421
421
 
422
422
  class __remote_gen_spec(typing_extensions.Protocol):
423
423
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -444,7 +444,7 @@ class Function(
444
444
  """
445
445
  ...
446
446
 
447
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
447
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
448
448
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
449
449
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
450
450
 
@@ -467,7 +467,7 @@ class Function(
467
467
  """
468
468
  ...
469
469
 
470
- _experimental_spawn: ___experimental_spawn_spec[modal._functions.P, modal._functions.ReturnType]
470
+ _experimental_spawn: ___experimental_spawn_spec[modal._functions.ReturnType, modal._functions.P]
471
471
 
472
472
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER]):
473
473
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> None: ...
@@ -475,7 +475,7 @@ class Function(
475
475
 
476
476
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P]
477
477
 
478
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
478
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
479
479
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
480
480
  """Calls the function with the given arguments, without waiting for the results.
481
481
 
@@ -496,7 +496,7 @@ class Function(
496
496
  """
497
497
  ...
498
498
 
499
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType]
499
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P]
500
500
 
501
501
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
502
502
  """Return the inner Python object wrapped by this Modal Function."""
@@ -231,17 +231,6 @@ class Object:
231
231
  """mdmd:hidden"""
232
232
  ...
233
233
 
234
- class __get_dashboard_url_spec(typing_extensions.Protocol):
235
- def __call__(self, /) -> str:
236
- """mdmd:hidden"""
237
- ...
238
-
239
- async def aio(self, /) -> str:
240
- """mdmd:hidden"""
241
- ...
242
-
243
- get_dashboard_url: __get_dashboard_url_spec
244
-
245
234
  @property
246
235
  def client(self) -> modal.client.Client:
247
236
  """mdmd:hidden"""
@@ -340,7 +340,11 @@ async def _run_app(
340
340
 
341
341
  # Start logs loop
342
342
 
343
- logs_loop = tc.create_task(get_app_logs_loop(load_context.client, output_mgr, app_id=running_app.app_id))
343
+ logs_loop = tc.create_task(
344
+ get_app_logs_loop(
345
+ load_context.client, output_mgr, app_id=running_app.app_id, app_logs_url=running_app.app_logs_url
346
+ )
347
+ )
344
348
 
345
349
  try:
346
350
  # Create all members
@@ -1,6 +1,7 @@
1
1
  # Copyright Modal Labs 2023
2
2
  import itertools
3
3
  import os
4
+ import webbrowser
4
5
  from collections.abc import AsyncGenerator
5
6
  from typing import Optional
6
7
 
@@ -11,7 +12,6 @@ from modal_proto import api_pb2
11
12
 
12
13
  from ._output import make_console
13
14
  from ._utils.async_utils import synchronize_api
14
- from ._utils.browser_utils import open_url
15
15
  from ._utils.http_utils import run_temporary_http_server
16
16
  from .client import _Client
17
17
  from .config import DEFAULT_SERVER_URL, _lookup_workspace, _store_user_config, config, config_profiles, user_config_path
@@ -85,7 +85,7 @@ async def _new_token(
85
85
  async with token_flow.start(source, next_url) as (_, web_url, code):
86
86
  with console.status("Waiting for authentication in the web browser", spinner="dots"):
87
87
  # Open the web url in the browser
88
- if open_url(web_url):
88
+ if _open_url(web_url):
89
89
  console.print(
90
90
  "The web browser should have opened for you to authenticate and get an API token.\n"
91
91
  "If it didn't, please copy this URL into your web browser manually:\n"
@@ -176,3 +176,18 @@ async def _set_token(
176
176
  f"Warning: The {env_vars_str} environment variable{s} {verb} set; this will override your new credentials.",
177
177
  style="yellow",
178
178
  )
179
+
180
+
181
+ def _open_url(url: str) -> bool:
182
+ """Opens url in web browser, making sure we use a modern one (not Lynx etc)"""
183
+ if "PYTEST_CURRENT_TEST" in os.environ:
184
+ return False
185
+ try:
186
+ browser = webbrowser.get()
187
+ # zpresto defines `BROWSER=open` by default on macOS, which causes `webbrowser` to return `GenericBrowser`.
188
+ if isinstance(browser, webbrowser.GenericBrowser) and browser.name != "open":
189
+ return False
190
+ else:
191
+ return browser.open_new_tab(url)
192
+ except webbrowser.Error:
193
+ return False
@@ -71,3 +71,6 @@ async def _set_token(
71
71
  verify: bool = True,
72
72
  server_url: typing.Optional[str] = None,
73
73
  ): ...
74
+ def _open_url(url: str) -> bool:
75
+ """Opens url in web browser, making sure we use a modern one (not Lynx etc)"""
76
+ ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.3.2
3
+ Version: 1.3.2.dev0
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -111,7 +111,6 @@ modal/_utils/app_utils.py
111
111
  modal/_utils/async_utils.py
112
112
  modal/_utils/auth_token_manager.py
113
113
  modal/_utils/blob_utils.py
114
- modal/_utils/browser_utils.py
115
114
  modal/_utils/bytes_io_segment_payload.py
116
115
  modal/_utils/deprecation.py
117
116
  modal/_utils/docker_utils.py
@@ -150,7 +149,6 @@ modal/cli/app.py
150
149
  modal/cli/cluster.py
151
150
  modal/cli/config.py
152
151
  modal/cli/container.py
153
- modal/cli/dashboard.py
154
152
  modal/cli/dict.py
155
153
  modal/cli/entry_point.py
156
154
  modal/cli/environment.py
@@ -3690,15 +3690,6 @@ message WorkspaceBillingReportRequest {
3690
3690
  repeated string tag_names = 4;
3691
3691
  }
3692
3692
 
3693
- message WorkspaceDashboardUrlRequest {
3694
- string environment_name = 1;
3695
- }
3696
-
3697
- message WorkspaceDashboardUrlResponse {
3698
- string url = 1;
3699
- }
3700
-
3701
-
3702
3693
  message WorkspaceNameLookupResponse {
3703
3694
  string workspace_name = 1 [deprecated=true];
3704
3695
  string username = 2;
@@ -3933,6 +3924,5 @@ service ModalClient {
3933
3924
 
3934
3925
  // Workspaces
3935
3926
  rpc WorkspaceBillingReport(WorkspaceBillingReportRequest) returns (stream WorkspaceBillingReportItem);
3936
- rpc WorkspaceDashboardUrlGet(WorkspaceDashboardUrlRequest) returns (WorkspaceDashboardUrlResponse);
3937
3927
  rpc WorkspaceNameLookup(google.protobuf.Empty) returns (WorkspaceNameLookupResponse);
3938
3928
  }
@@ -715,10 +715,6 @@ class ModalClientBase(abc.ABC):
715
715
  async def WorkspaceBillingReport(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.WorkspaceBillingReportRequest, modal_proto.api_pb2.WorkspaceBillingReportItem]') -> None:
716
716
  pass
717
717
 
718
- @abc.abstractmethod
719
- async def WorkspaceDashboardUrlGet(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.WorkspaceDashboardUrlRequest, modal_proto.api_pb2.WorkspaceDashboardUrlResponse]') -> None:
720
- pass
721
-
722
718
  @abc.abstractmethod
723
719
  async def WorkspaceNameLookup(self, stream: 'grpclib.server.Stream[google.protobuf.empty_pb2.Empty, modal_proto.api_pb2.WorkspaceNameLookupResponse]') -> None:
724
720
  pass
@@ -1769,12 +1765,6 @@ class ModalClientBase(abc.ABC):
1769
1765
  modal_proto.api_pb2.WorkspaceBillingReportRequest,
1770
1766
  modal_proto.api_pb2.WorkspaceBillingReportItem,
1771
1767
  ),
1772
- '/modal.client.ModalClient/WorkspaceDashboardUrlGet': grpclib.const.Handler(
1773
- self.WorkspaceDashboardUrlGet,
1774
- grpclib.const.Cardinality.UNARY_UNARY,
1775
- modal_proto.api_pb2.WorkspaceDashboardUrlRequest,
1776
- modal_proto.api_pb2.WorkspaceDashboardUrlResponse,
1777
- ),
1778
1768
  '/modal.client.ModalClient/WorkspaceNameLookup': grpclib.const.Handler(
1779
1769
  self.WorkspaceNameLookup,
1780
1770
  grpclib.const.Cardinality.UNARY_UNARY,
@@ -2831,12 +2821,6 @@ class ModalClientStub:
2831
2821
  modal_proto.api_pb2.WorkspaceBillingReportRequest,
2832
2822
  modal_proto.api_pb2.WorkspaceBillingReportItem,
2833
2823
  )
2834
- self.WorkspaceDashboardUrlGet = grpclib.client.UnaryUnaryMethod(
2835
- channel,
2836
- '/modal.client.ModalClient/WorkspaceDashboardUrlGet',
2837
- modal_proto.api_pb2.WorkspaceDashboardUrlRequest,
2838
- modal_proto.api_pb2.WorkspaceDashboardUrlResponse,
2839
- )
2840
2824
  self.WorkspaceNameLookup = grpclib.client.UnaryUnaryMethod(
2841
2825
  channel,
2842
2826
  '/modal.client.ModalClient/WorkspaceNameLookup',