modal 1.0.3.dev10__py3-none-any.whl → 1.2.3.dev7__py3-none-any.whl

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.

Potentially problematic release.


This version of modal might be problematic. Click here for more details.

Files changed (160) hide show
  1. modal/__init__.py +0 -2
  2. modal/__main__.py +3 -4
  3. modal/_billing.py +80 -0
  4. modal/_clustered_functions.py +7 -3
  5. modal/_clustered_functions.pyi +15 -3
  6. modal/_container_entrypoint.py +51 -69
  7. modal/_functions.py +508 -240
  8. modal/_grpc_client.py +171 -0
  9. modal/_load_context.py +105 -0
  10. modal/_object.py +81 -21
  11. modal/_output.py +58 -45
  12. modal/_partial_function.py +48 -73
  13. modal/_pty.py +7 -3
  14. modal/_resolver.py +26 -46
  15. modal/_runtime/asgi.py +4 -3
  16. modal/_runtime/container_io_manager.py +358 -220
  17. modal/_runtime/container_io_manager.pyi +296 -101
  18. modal/_runtime/execution_context.py +18 -2
  19. modal/_runtime/execution_context.pyi +64 -7
  20. modal/_runtime/gpu_memory_snapshot.py +262 -57
  21. modal/_runtime/user_code_imports.py +28 -58
  22. modal/_serialization.py +90 -6
  23. modal/_traceback.py +42 -1
  24. modal/_tunnel.pyi +380 -12
  25. modal/_utils/async_utils.py +84 -29
  26. modal/_utils/auth_token_manager.py +111 -0
  27. modal/_utils/blob_utils.py +181 -58
  28. modal/_utils/deprecation.py +19 -0
  29. modal/_utils/function_utils.py +91 -47
  30. modal/_utils/grpc_utils.py +89 -66
  31. modal/_utils/mount_utils.py +26 -1
  32. modal/_utils/name_utils.py +17 -3
  33. modal/_utils/task_command_router_client.py +536 -0
  34. modal/_utils/time_utils.py +34 -6
  35. modal/app.py +256 -88
  36. modal/app.pyi +909 -92
  37. modal/billing.py +5 -0
  38. modal/builder/2025.06.txt +18 -0
  39. modal/builder/PREVIEW.txt +18 -0
  40. modal/builder/base-images.json +58 -0
  41. modal/cli/_download.py +19 -3
  42. modal/cli/_traceback.py +3 -2
  43. modal/cli/app.py +4 -4
  44. modal/cli/cluster.py +15 -7
  45. modal/cli/config.py +5 -3
  46. modal/cli/container.py +7 -6
  47. modal/cli/dict.py +22 -16
  48. modal/cli/entry_point.py +12 -5
  49. modal/cli/environment.py +5 -4
  50. modal/cli/import_refs.py +3 -3
  51. modal/cli/launch.py +102 -5
  52. modal/cli/network_file_system.py +11 -12
  53. modal/cli/profile.py +3 -2
  54. modal/cli/programs/launch_instance_ssh.py +94 -0
  55. modal/cli/programs/run_jupyter.py +1 -1
  56. modal/cli/programs/run_marimo.py +95 -0
  57. modal/cli/programs/vscode.py +1 -1
  58. modal/cli/queues.py +57 -26
  59. modal/cli/run.py +91 -23
  60. modal/cli/secret.py +48 -22
  61. modal/cli/token.py +7 -8
  62. modal/cli/utils.py +4 -7
  63. modal/cli/volume.py +31 -25
  64. modal/client.py +15 -85
  65. modal/client.pyi +183 -62
  66. modal/cloud_bucket_mount.py +5 -3
  67. modal/cloud_bucket_mount.pyi +197 -5
  68. modal/cls.py +200 -126
  69. modal/cls.pyi +446 -68
  70. modal/config.py +29 -11
  71. modal/container_process.py +319 -19
  72. modal/container_process.pyi +190 -20
  73. modal/dict.py +290 -71
  74. modal/dict.pyi +835 -83
  75. modal/environments.py +15 -27
  76. modal/environments.pyi +46 -24
  77. modal/exception.py +14 -2
  78. modal/experimental/__init__.py +194 -40
  79. modal/experimental/flash.py +618 -0
  80. modal/experimental/flash.pyi +380 -0
  81. modal/experimental/ipython.py +11 -7
  82. modal/file_io.py +29 -36
  83. modal/file_io.pyi +251 -53
  84. modal/file_pattern_matcher.py +56 -16
  85. modal/functions.pyi +673 -92
  86. modal/gpu.py +1 -1
  87. modal/image.py +528 -176
  88. modal/image.pyi +1572 -145
  89. modal/io_streams.py +458 -128
  90. modal/io_streams.pyi +433 -52
  91. modal/mount.py +216 -151
  92. modal/mount.pyi +225 -78
  93. modal/network_file_system.py +45 -62
  94. modal/network_file_system.pyi +277 -56
  95. modal/object.pyi +93 -17
  96. modal/parallel_map.py +942 -129
  97. modal/parallel_map.pyi +294 -15
  98. modal/partial_function.py +0 -2
  99. modal/partial_function.pyi +234 -19
  100. modal/proxy.py +17 -8
  101. modal/proxy.pyi +36 -3
  102. modal/queue.py +270 -65
  103. modal/queue.pyi +817 -57
  104. modal/runner.py +115 -101
  105. modal/runner.pyi +205 -49
  106. modal/sandbox.py +512 -136
  107. modal/sandbox.pyi +845 -111
  108. modal/schedule.py +1 -1
  109. modal/secret.py +300 -70
  110. modal/secret.pyi +589 -34
  111. modal/serving.py +7 -11
  112. modal/serving.pyi +7 -8
  113. modal/snapshot.py +11 -8
  114. modal/snapshot.pyi +25 -4
  115. modal/token_flow.py +4 -4
  116. modal/token_flow.pyi +28 -8
  117. modal/volume.py +416 -158
  118. modal/volume.pyi +1117 -121
  119. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +10 -9
  120. modal-1.2.3.dev7.dist-info/RECORD +195 -0
  121. modal_docs/mdmd/mdmd.py +17 -4
  122. modal_proto/api.proto +534 -79
  123. modal_proto/api_grpc.py +337 -1
  124. modal_proto/api_pb2.py +1522 -968
  125. modal_proto/api_pb2.pyi +1619 -134
  126. modal_proto/api_pb2_grpc.py +699 -4
  127. modal_proto/api_pb2_grpc.pyi +226 -14
  128. modal_proto/modal_api_grpc.py +175 -154
  129. modal_proto/sandbox_router.proto +145 -0
  130. modal_proto/sandbox_router_grpc.py +105 -0
  131. modal_proto/sandbox_router_pb2.py +149 -0
  132. modal_proto/sandbox_router_pb2.pyi +333 -0
  133. modal_proto/sandbox_router_pb2_grpc.py +203 -0
  134. modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
  135. modal_proto/task_command_router.proto +144 -0
  136. modal_proto/task_command_router_grpc.py +105 -0
  137. modal_proto/task_command_router_pb2.py +149 -0
  138. modal_proto/task_command_router_pb2.pyi +333 -0
  139. modal_proto/task_command_router_pb2_grpc.py +203 -0
  140. modal_proto/task_command_router_pb2_grpc.pyi +75 -0
  141. modal_version/__init__.py +1 -1
  142. modal/requirements/PREVIEW.txt +0 -16
  143. modal/requirements/base-images.json +0 -26
  144. modal-1.0.3.dev10.dist-info/RECORD +0 -179
  145. modal_proto/modal_options_grpc.py +0 -3
  146. modal_proto/options.proto +0 -19
  147. modal_proto/options_grpc.py +0 -3
  148. modal_proto/options_pb2.py +0 -35
  149. modal_proto/options_pb2.pyi +0 -20
  150. modal_proto/options_pb2_grpc.py +0 -4
  151. modal_proto/options_pb2_grpc.pyi +0 -7
  152. /modal/{requirements → builder}/2023.12.312.txt +0 -0
  153. /modal/{requirements → builder}/2023.12.txt +0 -0
  154. /modal/{requirements → builder}/2024.04.txt +0 -0
  155. /modal/{requirements → builder}/2024.10.txt +0 -0
  156. /modal/{requirements → builder}/README.md +0 -0
  157. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
  158. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
  159. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
  160. {modal-1.0.3.dev10.dist-info → modal-1.2.3.dev7.dist-info}/top_level.txt +0 -0
modal/client.py CHANGED
@@ -6,14 +6,7 @@ import sys
6
6
  import urllib.parse
7
7
  import warnings
8
8
  from collections.abc import AsyncGenerator, AsyncIterator, Collection, Mapping
9
- from typing import (
10
- Any,
11
- ClassVar,
12
- Generic,
13
- Optional,
14
- TypeVar,
15
- Union,
16
- )
9
+ from typing import Any, ClassVar, Optional, TypeVar, Union
17
10
 
18
11
  import grpclib.client
19
12
  from google.protobuf import empty_pb2
@@ -27,7 +20,8 @@ from modal_version import __version__
27
20
  from ._traceback import print_server_warnings
28
21
  from ._utils import async_utils
29
22
  from ._utils.async_utils import TaskContext, synchronize_api
30
- from ._utils.grpc_utils import ConnectionManager, retry_transient_errors
23
+ from ._utils.auth_token_manager import _AuthTokenManager
24
+ from ._utils.grpc_utils import ConnectionManager
31
25
  from .config import _check_config, _is_remote, config, logger
32
26
  from .exception import AuthError, ClientClosed
33
27
 
@@ -78,6 +72,7 @@ class _Client:
78
72
  _cancellation_context: TaskContext
79
73
  _cancellation_context_event_loop: asyncio.AbstractEventLoop = None
80
74
  _stub: Optional[api_grpc.ModalClientStub]
75
+ _auth_token_manager: _AuthTokenManager = None
81
76
  _snapshotted: bool
82
77
 
83
78
  def __init__(
@@ -96,6 +91,7 @@ class _Client:
96
91
  self.version = version
97
92
  self._closed = False
98
93
  self._stub: Optional[modal_api_grpc.ModalClientModal] = None
94
+ self._auth_token_manager: Optional[_AuthTokenManager] = None
99
95
  self._snapshotted = False
100
96
  self._owner_pid = None
101
97
 
@@ -133,9 +129,9 @@ class _Client:
133
129
  self._cancellation_context = TaskContext(grace=0.5) # allow running rpcs to finish in 0.5s when closing client
134
130
  self._cancellation_context_event_loop = asyncio.get_running_loop()
135
131
  await self._cancellation_context.__aenter__()
136
-
137
132
  self._connection_manager = ConnectionManager(client=self, metadata=metadata)
138
133
  self._stub = await self.get_stub(self.server_url)
134
+ self._auth_token_manager = _AuthTokenManager(self.stub)
139
135
  self._owner_pid = os.getpid()
140
136
 
141
137
  async def _close(self, prep_for_restore: bool = False):
@@ -155,7 +151,7 @@ class _Client:
155
151
  async def hello(self):
156
152
  """Connect to server and retrieve version information; raise appropriate error for various failures."""
157
153
  logger.debug(f"Client ({id(self)}): Starting")
158
- resp = await retry_transient_errors(self.stub.ClientHello, empty_pb2.Empty())
154
+ resp = await self.stub.ClientHello(empty_pb2.Empty())
159
155
  print_server_warnings(resp.server_warnings)
160
156
 
161
157
  async def __aenter__(self):
@@ -264,6 +260,14 @@ class _Client:
264
260
  # Just used from tests.
265
261
  cls._client_from_env = client
266
262
 
263
+ async def get_input_plane_metadata(self, input_plane_region: str) -> list[tuple[str, str]]:
264
+ assert self._auth_token_manager, "Client must have an instance of auth token manager."
265
+ token = await self._auth_token_manager.get_token()
266
+ return [
267
+ ("x-modal-input-plane-region", input_plane_region),
268
+ ("x-modal-auth-token", token),
269
+ ]
270
+
267
271
  async def _call_safely(self, coro, readable_method: str):
268
272
  """Runs coroutine wrapped in a task that's part of the client's task context
269
273
 
@@ -350,77 +354,3 @@ class _Client:
350
354
 
351
355
 
352
356
  Client = synchronize_api(_Client)
353
-
354
-
355
- class UnaryUnaryWrapper(Generic[RequestType, ResponseType]):
356
- # Calls a grpclib.UnaryUnaryMethod using a specific Client instance, respecting
357
- # if that client is closed etc. and possibly introducing Modal-specific retry logic
358
- wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]
359
- client: _Client
360
-
361
- def __init__(
362
- self,
363
- wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
364
- client: _Client,
365
- server_url: str,
366
- ):
367
- self.wrapped_method = wrapped_method
368
- self.client = client
369
- self.server_url = server_url
370
-
371
- @property
372
- def name(self) -> str:
373
- return self.wrapped_method.name
374
-
375
- async def __call__(
376
- self,
377
- req: RequestType,
378
- *,
379
- timeout: Optional[float] = None,
380
- metadata: Optional[_MetadataLike] = None,
381
- ) -> ResponseType:
382
- if self.client._snapshotted:
383
- logger.debug(f"refreshing client after snapshot for {self.name.rsplit('/', 1)[1]}")
384
- self.client = await _Client.from_env()
385
-
386
- # Note: We override the grpclib method's channel (see grpclib's code [1]). I think this is fine
387
- # since grpclib's code doesn't seem to change very much, but we could also recreate the
388
- # grpclib stub if we aren't comfortable with this. The downside is then we need to cache
389
- # the grpclib stub so the rest of our code becomes a bit more complicated.
390
- #
391
- # We need to override the channel because after the process is forked or the client is
392
- # snapshotted, the existing channel may be stale / unusable.
393
- #
394
- # [1]: https://github.com/vmagamedov/grpclib/blob/62f968a4c84e3f64e6966097574ff0a59969ea9b/grpclib/client.py#L844
395
- self.wrapped_method.channel = await self.client._get_channel(self.server_url)
396
- return await self.client._call_unary(self.wrapped_method, req, timeout=timeout, metadata=metadata)
397
-
398
-
399
- class UnaryStreamWrapper(Generic[RequestType, ResponseType]):
400
- wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType]
401
-
402
- def __init__(
403
- self,
404
- wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
405
- client: _Client,
406
- server_url: str,
407
- ):
408
- self.wrapped_method = wrapped_method
409
- self.client = client
410
- self.server_url = server_url
411
-
412
- @property
413
- def name(self) -> str:
414
- return self.wrapped_method.name
415
-
416
- async def unary_stream(
417
- self,
418
- request,
419
- metadata: Optional[Any] = None,
420
- ):
421
- if self.client._snapshotted:
422
- logger.debug(f"refreshing client after snapshot for {self.name.rsplit('/', 1)[1]}")
423
- self.client = await _Client.from_env()
424
- self.wrapped_method.channel = await self.client._get_channel(self.server_url)
425
- async for response in self.client._call_stream(self.wrapped_method, request, metadata=metadata):
426
- yield response
modal/client.pyi CHANGED
@@ -4,6 +4,7 @@ import collections.abc
4
4
  import google.protobuf.message
5
5
  import grpclib.client
6
6
  import modal._utils.async_utils
7
+ import modal._utils.auth_token_manager
7
8
  import modal_proto.api_grpc
8
9
  import modal_proto.modal_api_grpc
9
10
  import synchronicity.combined_types
@@ -24,6 +25,7 @@ class _Client:
24
25
  _cancellation_context: modal._utils.async_utils.TaskContext
25
26
  _cancellation_context_event_loop: asyncio.events.AbstractEventLoop
26
27
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
28
+ _auth_token_manager: modal._utils.auth_token_manager._AuthTokenManager
27
29
  _snapshotted: bool
28
30
 
29
31
  def __init__(
@@ -31,28 +33,95 @@ class _Client:
31
33
  server_url: str,
32
34
  client_type: int,
33
35
  credentials: typing.Optional[tuple[str, str]],
34
- version: str = "1.0.3.dev10",
35
- ): ...
36
+ version: str = "1.2.3.dev7",
37
+ ):
38
+ """mdmd:hidden
39
+ The Modal client object is not intended to be instantiated directly by users.
40
+ """
41
+ ...
42
+
36
43
  def is_closed(self) -> bool: ...
37
44
  @property
38
- def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal: ...
39
- async def get_stub(self, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal: ...
45
+ def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal:
46
+ """mdmd:hidden
47
+ The default stub. Stubs can safely be used across forks / client snapshots.
48
+
49
+ This is useful if you want to make requests to the default Modal server in us-east, for example
50
+ control plane requests.
51
+
52
+ This is equivalent to client.get_stub(default_server_url), but it's cached, so it's a bit faster.
53
+ """
54
+ ...
55
+
56
+ async def get_stub(self, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal:
57
+ """mdmd:hidden
58
+ Get a stub for a specific server URL. Stubs can safely be used across forks / client snapshots.
59
+
60
+ This is useful if you want to make requests to a regional Modal server, for example low-latency
61
+ function calls in us-west.
62
+
63
+ This function is O(n) where n is the number of RPCs in ModalClient.
64
+ """
65
+ ...
66
+
40
67
  async def _open(self): ...
41
68
  async def _close(self, prep_for_restore: bool = False): ...
42
- async def hello(self): ...
69
+ async def hello(self):
70
+ """Connect to server and retrieve version information; raise appropriate error for various failures."""
71
+ ...
72
+
43
73
  async def __aenter__(self): ...
44
74
  async def __aexit__(self, exc_type, exc, tb): ...
45
75
  @classmethod
46
- def anonymous(cls, server_url: str) -> typing.AsyncContextManager[_Client]: ...
76
+ def anonymous(cls, server_url: str) -> typing.AsyncContextManager[_Client]:
77
+ """mdmd:hidden
78
+ Create a connection with no credentials; to be used for token creation.
79
+ """
80
+ ...
81
+
47
82
  @classmethod
48
- async def from_env(cls, _override_config=None) -> _Client: ...
83
+ async def from_env(cls, _override_config=None) -> _Client:
84
+ """mdmd:hidden
85
+ Singleton that is instantiated from the Modal config and reused on subsequent calls.
86
+ """
87
+ ...
88
+
49
89
  @classmethod
50
- async def from_credentials(cls, token_id: str, token_secret: str) -> _Client: ...
90
+ async def from_credentials(cls, token_id: str, token_secret: str) -> _Client:
91
+ """Constructor based on token credentials; useful for managing Modal on behalf of third-party users.
92
+
93
+ **Usage:**
94
+
95
+ ```python notest
96
+ client = modal.Client.from_credentials("my_token_id", "my_token_secret")
97
+
98
+ modal.Sandbox.create("echo", "hi", client=client, app=app)
99
+ ```
100
+ """
101
+ ...
102
+
51
103
  @classmethod
52
- async def verify(cls, server_url: str, credentials: tuple[str, str]) -> None: ...
104
+ async def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
105
+ """mdmd:hidden
106
+ Check whether can the client can connect to this server with these credentials; raise if not.
107
+ """
108
+ ...
109
+
53
110
  @classmethod
54
- def set_env_client(cls, client: typing.Optional[_Client]): ...
55
- async def _call_safely(self, coro, readable_method: str): ...
111
+ def set_env_client(cls, client: typing.Optional[_Client]):
112
+ """mdmd:hidden"""
113
+ ...
114
+
115
+ async def get_input_plane_metadata(self, input_plane_region: str) -> list[tuple[str, str]]: ...
116
+ async def _call_safely(self, coro, readable_method: str):
117
+ """Runs coroutine wrapped in a task that's part of the client's task context
118
+
119
+ * Raises ClientClosed in case the client is closed while the coroutine is executed
120
+ * Logs warning if call is made outside of the event loop that the client is running in,
121
+ and execute without the cancellation context in that case
122
+ """
123
+ ...
124
+
56
125
  async def _reset_on_pid_change(self): ...
57
126
  async def _get_channel(self, server_url: str) -> grpclib.client.Channel: ...
58
127
  async def _call_unary(
@@ -87,6 +156,7 @@ class Client:
87
156
  _cancellation_context: modal._utils.async_utils.TaskContext
88
157
  _cancellation_context_event_loop: asyncio.events.AbstractEventLoop
89
158
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
159
+ _auth_token_manager: modal._utils.auth_token_manager._AuthTokenManager
90
160
  _snapshotted: bool
91
161
 
92
162
  def __init__(
@@ -94,15 +164,48 @@ class Client:
94
164
  server_url: str,
95
165
  client_type: int,
96
166
  credentials: typing.Optional[tuple[str, str]],
97
- version: str = "1.0.3.dev10",
98
- ): ...
167
+ version: str = "1.2.3.dev7",
168
+ ):
169
+ """mdmd:hidden
170
+ The Modal client object is not intended to be instantiated directly by users.
171
+ """
172
+ ...
173
+
99
174
  def is_closed(self) -> bool: ...
100
175
  @property
101
- def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal: ...
176
+ def stub(self) -> modal_proto.modal_api_grpc.ModalClientModal:
177
+ """mdmd:hidden
178
+ The default stub. Stubs can safely be used across forks / client snapshots.
179
+
180
+ This is useful if you want to make requests to the default Modal server in us-east, for example
181
+ control plane requests.
182
+
183
+ This is equivalent to client.get_stub(default_server_url), but it's cached, so it's a bit faster.
184
+ """
185
+ ...
102
186
 
103
187
  class __get_stub_spec(typing_extensions.Protocol[SUPERSELF]):
104
- def __call__(self, /, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal: ...
105
- async def aio(self, /, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal: ...
188
+ def __call__(self, /, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal:
189
+ """mdmd:hidden
190
+ Get a stub for a specific server URL. Stubs can safely be used across forks / client snapshots.
191
+
192
+ This is useful if you want to make requests to a regional Modal server, for example low-latency
193
+ function calls in us-west.
194
+
195
+ This function is O(n) where n is the number of RPCs in ModalClient.
196
+ """
197
+ ...
198
+
199
+ async def aio(self, /, server_url: str) -> modal_proto.modal_api_grpc.ModalClientModal:
200
+ """mdmd:hidden
201
+ Get a stub for a specific server URL. Stubs can safely be used across forks / client snapshots.
202
+
203
+ This is useful if you want to make requests to a regional Modal server, for example low-latency
204
+ function calls in us-west.
205
+
206
+ This function is O(n) where n is the number of RPCs in ModalClient.
207
+ """
208
+ ...
106
209
 
107
210
  get_stub: __get_stub_spec[typing_extensions.Self]
108
211
 
@@ -119,8 +222,13 @@ class Client:
119
222
  _close: ___close_spec[typing_extensions.Self]
120
223
 
121
224
  class __hello_spec(typing_extensions.Protocol[SUPERSELF]):
122
- def __call__(self, /): ...
123
- async def aio(self, /): ...
225
+ def __call__(self, /):
226
+ """Connect to server and retrieve version information; raise appropriate error for various failures."""
227
+ ...
228
+
229
+ async def aio(self, /):
230
+ """Connect to server and retrieve version information; raise appropriate error for various failures."""
231
+ ...
124
232
 
125
233
  hello: __hello_spec[typing_extensions.Self]
126
234
 
@@ -129,19 +237,69 @@ class Client:
129
237
  def __exit__(self, exc_type, exc, tb): ...
130
238
  async def __aexit__(self, exc_type, exc, tb): ...
131
239
  @classmethod
132
- def anonymous(cls, server_url: str) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Client]: ...
240
+ def anonymous(cls, server_url: str) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Client]:
241
+ """mdmd:hidden
242
+ Create a connection with no credentials; to be used for token creation.
243
+ """
244
+ ...
245
+
133
246
  @classmethod
134
- def from_env(cls, _override_config=None) -> Client: ...
247
+ def from_env(cls, _override_config=None) -> Client:
248
+ """mdmd:hidden
249
+ Singleton that is instantiated from the Modal config and reused on subsequent calls.
250
+ """
251
+ ...
252
+
135
253
  @classmethod
136
- def from_credentials(cls, token_id: str, token_secret: str) -> Client: ...
254
+ def from_credentials(cls, token_id: str, token_secret: str) -> Client:
255
+ """Constructor based on token credentials; useful for managing Modal on behalf of third-party users.
256
+
257
+ **Usage:**
258
+
259
+ ```python notest
260
+ client = modal.Client.from_credentials("my_token_id", "my_token_secret")
261
+
262
+ modal.Sandbox.create("echo", "hi", client=client, app=app)
263
+ ```
264
+ """
265
+ ...
266
+
137
267
  @classmethod
138
- def verify(cls, server_url: str, credentials: tuple[str, str]) -> None: ...
268
+ def verify(cls, server_url: str, credentials: tuple[str, str]) -> None:
269
+ """mdmd:hidden
270
+ Check whether can the client can connect to this server with these credentials; raise if not.
271
+ """
272
+ ...
273
+
139
274
  @classmethod
140
- def set_env_client(cls, client: typing.Optional[Client]): ...
275
+ def set_env_client(cls, client: typing.Optional[Client]):
276
+ """mdmd:hidden"""
277
+ ...
278
+
279
+ class __get_input_plane_metadata_spec(typing_extensions.Protocol[SUPERSELF]):
280
+ def __call__(self, /, input_plane_region: str) -> list[tuple[str, str]]: ...
281
+ async def aio(self, /, input_plane_region: str) -> list[tuple[str, str]]: ...
282
+
283
+ get_input_plane_metadata: __get_input_plane_metadata_spec[typing_extensions.Self]
141
284
 
142
285
  class ___call_safely_spec(typing_extensions.Protocol[SUPERSELF]):
143
- def __call__(self, /, coro, readable_method: str): ...
144
- async def aio(self, /, coro, readable_method: str): ...
286
+ def __call__(self, /, coro, readable_method: str):
287
+ """Runs coroutine wrapped in a task that's part of the client's task context
288
+
289
+ * Raises ClientClosed in case the client is closed while the coroutine is executed
290
+ * Logs warning if call is made outside of the event loop that the client is running in,
291
+ and execute without the cancellation context in that case
292
+ """
293
+ ...
294
+
295
+ async def aio(self, /, coro, readable_method: str):
296
+ """Runs coroutine wrapped in a task that's part of the client's task context
297
+
298
+ * Raises ClientClosed in case the client is closed while the coroutine is executed
299
+ * Logs warning if call is made outside of the event loop that the client is running in,
300
+ and execute without the cancellation context in that case
301
+ """
302
+ ...
145
303
 
146
304
  _call_safely: ___call_safely_spec[typing_extensions.Self]
147
305
 
@@ -181,43 +339,6 @@ class Client:
181
339
  ],
182
340
  ) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
183
341
 
184
- class UnaryUnaryWrapper(typing.Generic[RequestType, ResponseType]):
185
- wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]
186
- client: _Client
187
-
188
- def __init__(
189
- self,
190
- wrapped_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
191
- client: _Client,
192
- server_url: str,
193
- ): ...
194
- @property
195
- def name(self) -> str: ...
196
- async def __call__(
197
- self,
198
- req: RequestType,
199
- *,
200
- timeout: typing.Optional[float] = None,
201
- metadata: typing.Union[
202
- collections.abc.Mapping[str, typing.Union[str, bytes]],
203
- collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
204
- None,
205
- ] = None,
206
- ) -> ResponseType: ...
207
-
208
- class UnaryStreamWrapper(typing.Generic[RequestType, ResponseType]):
209
- wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType]
210
-
211
- def __init__(
212
- self,
213
- wrapped_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
214
- client: _Client,
215
- server_url: str,
216
- ): ...
217
- @property
218
- def name(self) -> str: ...
219
- def unary_stream(self, request, metadata: typing.Optional[typing.Any] = None): ...
220
-
221
342
  HEARTBEAT_INTERVAL: float
222
343
 
223
344
  HEARTBEAT_TIMEOUT: float
@@ -1,6 +1,6 @@
1
1
  # Copyright Modal Labs 2022
2
2
  from dataclasses import dataclass
3
- from typing import Optional
3
+ from typing import Optional, Sequence
4
4
  from urllib.parse import urlparse
5
5
 
6
6
  from modal_proto import api_pb2
@@ -117,9 +117,10 @@ class _CloudBucketMount:
117
117
 
118
118
  read_only: bool = False
119
119
  requester_pays: bool = False
120
+ force_path_style: bool = False
120
121
 
121
122
 
122
- def cloud_bucket_mounts_to_proto(mounts: list[tuple[str, _CloudBucketMount]]) -> list[api_pb2.CloudBucketMount]:
123
+ def cloud_bucket_mounts_to_proto(mounts: Sequence[tuple[str, _CloudBucketMount]]) -> list[api_pb2.CloudBucketMount]:
123
124
  """Helper function to convert `CloudBucketMount` to a list of protobufs that can be passed to the server."""
124
125
  cloud_bucket_mounts: list[api_pb2.CloudBucketMount] = []
125
126
 
@@ -132,7 +133,7 @@ def cloud_bucket_mounts_to_proto(mounts: list[tuple[str, _CloudBucketMount]]) ->
132
133
  elif parse_result.hostname.endswith("storage.googleapis.com"):
133
134
  bucket_type = api_pb2.CloudBucketMount.BucketType.GCP
134
135
  else:
135
- logger.warning(
136
+ logger.info(
136
137
  "CloudBucketMount received unrecognized bucket endpoint URL. "
137
138
  "Assuming AWS S3 configuration as fallback."
138
139
  )
@@ -159,6 +160,7 @@ def cloud_bucket_mounts_to_proto(mounts: list[tuple[str, _CloudBucketMount]]) ->
159
160
  requester_pays=mount.requester_pays,
160
161
  key_prefix=key_prefix,
161
162
  oidc_auth_role_arn=mount.oidc_auth_role_arn,
163
+ force_path_style=mount.force_path_style,
162
164
  )
163
165
  cloud_bucket_mounts.append(cloud_bucket_mount)
164
166