modal 1.0.5.dev2__py3-none-any.whl → 1.0.5.dev4__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.
Files changed (50) hide show
  1. modal/_clustered_functions.pyi +13 -3
  2. modal/_functions.py +5 -4
  3. modal/_partial_function.py +1 -1
  4. modal/_runtime/container_io_manager.pyi +222 -40
  5. modal/_runtime/execution_context.pyi +60 -6
  6. modal/_tunnel.pyi +380 -12
  7. modal/app.py +4 -4
  8. modal/app.pyi +658 -48
  9. modal/cli/run.py +2 -1
  10. modal/client.pyi +224 -28
  11. modal/cloud_bucket_mount.pyi +192 -4
  12. modal/cls.py +3 -3
  13. modal/cls.pyi +442 -35
  14. modal/container_process.pyi +103 -14
  15. modal/dict.py +1 -1
  16. modal/dict.pyi +453 -51
  17. modal/environments.pyi +41 -9
  18. modal/exception.py +2 -2
  19. modal/file_io.pyi +236 -45
  20. modal/functions.pyi +545 -56
  21. modal/gpu.py +1 -1
  22. modal/image.py +1 -1
  23. modal/image.pyi +1256 -74
  24. modal/io_streams.pyi +342 -39
  25. modal/mount.pyi +261 -31
  26. modal/network_file_system.pyi +307 -26
  27. modal/object.pyi +48 -9
  28. modal/parallel_map.pyi +144 -14
  29. modal/partial_function.pyi +255 -14
  30. modal/proxy.py +1 -1
  31. modal/proxy.pyi +28 -3
  32. modal/queue.py +1 -1
  33. modal/queue.pyi +447 -30
  34. modal/runner.pyi +160 -22
  35. modal/sandbox.py +7 -7
  36. modal/sandbox.pyi +310 -50
  37. modal/schedule.py +1 -1
  38. modal/secret.py +2 -2
  39. modal/secret.pyi +164 -15
  40. modal/snapshot.pyi +25 -4
  41. modal/token_flow.pyi +28 -8
  42. modal/volume.py +1 -1
  43. modal/volume.pyi +649 -59
  44. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/METADATA +1 -1
  45. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/RECORD +50 -50
  46. modal_version/__init__.py +1 -1
  47. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/WHEEL +0 -0
  48. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/entry_points.txt +0 -0
  49. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/licenses/LICENSE +0 -0
  50. {modal-1.0.5.dev2.dist-info → modal-1.0.5.dev4.dist-info}/top_level.txt +0 -0
modal/parallel_map.pyi CHANGED
@@ -12,6 +12,7 @@ import typing
12
12
  import typing_extensions
13
13
 
14
14
  class _SynchronizedQueue:
15
+ """mdmd:hidden"""
15
16
  async def init(self): ...
16
17
  async def put(self, item): ...
17
18
  async def get(self): ...
@@ -19,7 +20,10 @@ class _SynchronizedQueue:
19
20
  SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
20
21
 
21
22
  class SynchronizedQueue:
22
- def __init__(self, /, *args, **kwargs): ...
23
+ """mdmd:hidden"""
24
+ def __init__(self, /, *args, **kwargs):
25
+ """Initialize self. See help(type(self)) for accurate signature."""
26
+ ...
23
27
 
24
28
  class __init_spec(typing_extensions.Protocol[SUPERSELF]):
25
29
  def __call__(self, /): ...
@@ -40,11 +44,21 @@ class SynchronizedQueue:
40
44
  get: __get_spec[typing_extensions.Self]
41
45
 
42
46
  class _OutputValue:
47
+ """_OutputValue(value: Any)"""
48
+
43
49
  value: typing.Any
44
50
 
45
- def __init__(self, value: typing.Any) -> None: ...
46
- def __repr__(self): ...
47
- def __eq__(self, other): ...
51
+ def __init__(self, value: typing.Any) -> None:
52
+ """Initialize self. See help(type(self)) for accurate signature."""
53
+ ...
54
+
55
+ def __repr__(self):
56
+ """Return repr(self)."""
57
+ ...
58
+
59
+ def __eq__(self, other):
60
+ """Return self==value."""
61
+ ...
48
62
 
49
63
  def _map_invocation(
50
64
  function: modal._functions._Function,
@@ -63,7 +77,20 @@ def _map_helper(
63
77
  order_outputs: bool = True,
64
78
  return_exceptions: bool = False,
65
79
  wrap_returned_exceptions: bool = True,
66
- ) -> typing.AsyncGenerator[typing.Any, None]: ...
80
+ ) -> typing.AsyncGenerator[typing.Any, None]:
81
+ """Core implementation that supports `_map_async()`, `_starmap_async()` and `_for_each_async()`.
82
+
83
+ Runs in an event loop on the main thread. Concurrently feeds new input to the input queue and yields available
84
+ outputs to the caller.
85
+
86
+ Note that since the iterator(s) can block, it's a bit opaque how often the event
87
+ loop decides to get a new input vs how often it will emit a new output.
88
+
89
+ We could make this explicit as an improvement or even let users decide what they
90
+ prefer: throughput (prioritize queueing inputs) or latency (prioritize yielding results)
91
+ """
92
+ ...
93
+
67
94
  def _map_async(
68
95
  self: modal.functions.Function,
69
96
  *input_iterators: typing.Union[typing.Iterable[typing.Any], typing.AsyncIterable[typing.Any]],
@@ -91,10 +118,81 @@ def _map_sync(
91
118
  order_outputs: bool = True,
92
119
  return_exceptions: bool = False,
93
120
  wrap_returned_exceptions: bool = True,
94
- ) -> modal._utils.async_utils.AsyncOrSyncIterable: ...
95
- async def _spawn_map_async(self, *input_iterators, kwargs={}) -> None: ...
96
- def _spawn_map_sync(self, *input_iterators, kwargs={}) -> None: ...
97
- def _for_each_sync(self, *input_iterators, kwargs={}, ignore_exceptions: bool = False): ...
121
+ ) -> modal._utils.async_utils.AsyncOrSyncIterable:
122
+ """Parallel map over a set of inputs.
123
+
124
+ Takes one iterator argument per argument in the function being mapped over.
125
+
126
+ Example:
127
+ ```python
128
+ @app.function()
129
+ def my_func(a):
130
+ return a ** 2
131
+
132
+
133
+ @app.local_entrypoint()
134
+ def main():
135
+ assert list(my_func.map([1, 2, 3, 4])) == [1, 4, 9, 16]
136
+ ```
137
+
138
+ If applied to a `app.function`, `map()` returns one result per input and the output order
139
+ is guaranteed to be the same as the input order. Set `order_outputs=False` to return results
140
+ in the order that they are completed instead.
141
+
142
+ `return_exceptions` can be used to treat exceptions as successful results:
143
+
144
+ ```python
145
+ @app.function()
146
+ def my_func(a):
147
+ if a == 2:
148
+ raise Exception("ohno")
149
+ return a ** 2
150
+
151
+
152
+ @app.local_entrypoint()
153
+ def main():
154
+ # [0, 1, UserCodeException(Exception('ohno'))]
155
+ print(list(my_func.map(range(3), return_exceptions=True)))
156
+ ```
157
+ """
158
+ ...
159
+
160
+ async def _spawn_map_async(self, *input_iterators, kwargs={}) -> None:
161
+ """This runs in an event loop on the main thread. It consumes inputs from the input iterators and creates async
162
+ function calls for each.
163
+ """
164
+ ...
165
+
166
+ def _spawn_map_sync(self, *input_iterators, kwargs={}) -> None:
167
+ """Spawn parallel execution over a set of inputs, exiting as soon as the inputs are created (without waiting
168
+ for the map to complete).
169
+
170
+ Takes one iterator argument per argument in the function being mapped over.
171
+
172
+ Example:
173
+ ```python
174
+ @app.function()
175
+ def my_func(a):
176
+ return a ** 2
177
+
178
+
179
+ @app.local_entrypoint()
180
+ def main():
181
+ my_func.spawn_map([1, 2, 3, 4])
182
+ ```
183
+
184
+ Programmatic retrieval of results will be supported in a future update.
185
+ """
186
+ ...
187
+
188
+ def _for_each_sync(self, *input_iterators, kwargs={}, ignore_exceptions: bool = False):
189
+ """Execute function for all inputs, ignoring outputs. Waits for completion of the inputs.
190
+
191
+ Convenient alias for `.map()` in cases where the function just needs to be called.
192
+ as the caller doesn't have to consume the generator to process the inputs.
193
+ """
194
+ ...
195
+
98
196
  def _starmap_sync(
99
197
  self,
100
198
  input_iterator: typing.Iterable[typing.Sequence[typing.Any]],
@@ -103,7 +201,24 @@ def _starmap_sync(
103
201
  order_outputs: bool = True,
104
202
  return_exceptions: bool = False,
105
203
  wrap_returned_exceptions: bool = True,
106
- ) -> modal._utils.async_utils.AsyncOrSyncIterable: ...
204
+ ) -> modal._utils.async_utils.AsyncOrSyncIterable:
205
+ """Like `map`, but spreads arguments over multiple function arguments.
206
+
207
+ Assumes every input is a sequence (e.g. a tuple).
208
+
209
+ Example:
210
+ ```python
211
+ @app.function()
212
+ def my_func(a, b):
213
+ return a + b
214
+
215
+
216
+ @app.local_entrypoint()
217
+ def main():
218
+ assert list(my_func.starmap([(1, 2), (3, 4)])) == [3, 7]
219
+ ```
220
+ """
221
+ ...
107
222
 
108
223
  class _MapItemState(enum.Enum):
109
224
  # The input is being sent the server with a PutInputs request, but the response has not been received yet.
@@ -140,7 +255,10 @@ class _MapItemContext:
140
255
  input: modal_proto.api_pb2.FunctionInput,
141
256
  retry_manager: modal.retries.RetryManager,
142
257
  sync_client_retries_enabled: bool,
143
- ): ...
258
+ ):
259
+ """Initialize self. See help(type(self)) for accurate signature."""
260
+ ...
261
+
144
262
  def handle_put_inputs_response(self, item: modal_proto.api_pb2.FunctionPutInputsResponseItem): ...
145
263
  async def handle_get_outputs_response(
146
264
  self,
@@ -148,7 +266,13 @@ class _MapItemContext:
148
266
  now_seconds: int,
149
267
  function_call_invocation_type: int,
150
268
  retry_queue: modal._utils.async_utils.TimestampPriorityQueue,
151
- ) -> _OutputType: ...
269
+ ) -> _OutputType:
270
+ """Processes the output, and determines if it is complete or needs to be retried.
271
+
272
+ Return True if input state was changed to COMPLETE, otherwise False.
273
+ """
274
+ ...
275
+
152
276
  async def prepare_item_for_retry(self) -> modal_proto.api_pb2.FunctionRetryInputsItem: ...
153
277
  def handle_retry_response(self, input_jwt: str): ...
154
278
 
@@ -160,12 +284,18 @@ class _MapItemsManager:
160
284
  retry_queue: modal._utils.async_utils.TimestampPriorityQueue,
161
285
  sync_client_retries_enabled: bool,
162
286
  max_inputs_outstanding: int,
163
- ): ...
287
+ ):
288
+ """Initialize self. See help(type(self)) for accurate signature."""
289
+ ...
290
+
164
291
  async def add_items(self, items: list[modal_proto.api_pb2.FunctionPutInputsItem]): ...
165
292
  async def prepare_items_for_retry(
166
293
  self, retriable_idxs: list[int]
167
294
  ) -> list[modal_proto.api_pb2.FunctionRetryInputsItem]: ...
168
- def get_input_jwts_waiting_for_output(self) -> list[str]: ...
295
+ def get_input_jwts_waiting_for_output(self) -> list[str]:
296
+ """Returns a list of input_jwts for inputs that are waiting for output."""
297
+ ...
298
+
169
299
  def _remove_item(self, item_idx: int): ...
170
300
  def get_item_context(self, item_idx: int) -> _MapItemContext: ...
171
301
  def handle_put_inputs_response(self, items: list[modal_proto.api_pb2.FunctionPutInputsResponseItem]): ...
@@ -9,6 +9,11 @@ class PartialFunction(
9
9
  modal._partial_function.P, modal._partial_function.ReturnType, modal._partial_function.OriginalReturnType
10
10
  ]
11
11
  ):
12
+ """Object produced by a decorator in the `modal` namespace
13
+
14
+ The object will eventually by consumed by an App decorator.
15
+ """
16
+
12
17
  raw_f: typing.Optional[collections.abc.Callable[modal._partial_function.P, modal._partial_function.ReturnType]]
13
18
  user_cls: typing.Optional[type]
14
19
  flags: modal._partial_function._PartialFunctionFlags
@@ -27,11 +32,20 @@ class PartialFunction(
27
32
  self,
28
33
  flags: modal._partial_function._PartialFunctionFlags,
29
34
  params: modal._partial_function._PartialFunctionParams,
30
- ) -> typing_extensions.Self: ...
31
- def validate_flag_composition(self) -> None: ...
35
+ ) -> typing_extensions.Self:
36
+ """Implement decorator composition by combining the flags and params."""
37
+ ...
38
+
39
+ def validate_flag_composition(self) -> None:
40
+ """Validate decorator composition based on PartialFunctionFlags."""
41
+ ...
42
+
32
43
  def validate_obj_compatibility(
33
44
  self, decorator_name: str, require_sync: bool = False, require_nullary: bool = False
34
- ) -> None: ...
45
+ ) -> None:
46
+ """Enforce compatibility with the wrapped object; called from individual decorator functions."""
47
+ ...
48
+
35
49
  def _get_raw_f(self) -> collections.abc.Callable[modal._partial_function.P, modal._partial_function.ReturnType]: ...
36
50
  def _is_web_endpoint(self) -> bool: ...
37
51
  def __get__(
@@ -43,7 +57,22 @@ class PartialFunction(
43
57
 
44
58
  def method(
45
59
  _warn_parentheses_missing=None, *, is_generator: typing.Optional[bool] = None
46
- ) -> modal._partial_function._MethodDecoratorType: ...
60
+ ) -> modal._partial_function._MethodDecoratorType:
61
+ """Decorator for methods that should be transformed into a Modal Function registered against this class's App.
62
+
63
+ **Usage:**
64
+
65
+ ```python
66
+ @app.cls(cpu=8)
67
+ class MyCls:
68
+
69
+ @modal.method()
70
+ def f(self):
71
+ ...
72
+ ```
73
+ """
74
+ ...
75
+
47
76
  def web_endpoint(
48
77
  _warn_parentheses_missing=None,
49
78
  *,
@@ -62,7 +91,25 @@ def web_endpoint(
62
91
  ]
63
92
  ],
64
93
  PartialFunction[modal._partial_function.P, modal._partial_function.ReturnType, modal._partial_function.ReturnType],
65
- ]: ...
94
+ ]:
95
+ """Register a basic web endpoint with this application.
96
+
97
+ DEPRECATED: This decorator has been renamed to `@modal.fastapi_endpoint`.
98
+
99
+ This is the simple way to create a web endpoint on Modal. The function
100
+ behaves as a [FastAPI](https://fastapi.tiangolo.com/) handler and should
101
+ return a response object to the caller.
102
+
103
+ Endpoints created with `@modal.web_endpoint` are meant to be simple, single
104
+ request handlers and automatically have
105
+ [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) enabled.
106
+ For more flexibility, use `@modal.asgi_app`.
107
+
108
+ To learn how to use Modal with popular web frameworks, see the
109
+ [guide on web endpoints](https://modal.com/docs/guide/webhooks).
110
+ """
111
+ ...
112
+
66
113
  def fastapi_endpoint(
67
114
  _warn_parentheses_missing=None,
68
115
  *,
@@ -81,7 +128,24 @@ def fastapi_endpoint(
81
128
  ]
82
129
  ],
83
130
  PartialFunction[modal._partial_function.P, modal._partial_function.ReturnType, modal._partial_function.ReturnType],
84
- ]: ...
131
+ ]:
132
+ """Convert a function into a basic web endpoint by wrapping it with a FastAPI App.
133
+
134
+ Modal will internally use [FastAPI](https://fastapi.tiangolo.com/) to expose a
135
+ simple, single request handler. If you are defining your own `FastAPI` application
136
+ (e.g. if you want to define multiple routes), use `@modal.asgi_app` instead.
137
+
138
+ The endpoint created with this decorator will automatically have
139
+ [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) enabled
140
+ and can leverage many of FastAPI's features.
141
+
142
+ For more information on using Modal with popular web frameworks, see our
143
+ [guide on web endpoints](https://modal.com/docs/guide/webhooks).
144
+
145
+ *Added in v0.73.82*: This function replaces the deprecated `@web_endpoint` decorator.
146
+ """
147
+ ...
148
+
85
149
  def asgi_app(
86
150
  _warn_parentheses_missing=None,
87
151
  *,
@@ -97,7 +161,30 @@ def asgi_app(
97
161
  ]
98
162
  ],
99
163
  PartialFunction,
100
- ]: ...
164
+ ]:
165
+ """Decorator for registering an ASGI app with a Modal function.
166
+
167
+ Asynchronous Server Gateway Interface (ASGI) is a standard for Python
168
+ synchronous and asynchronous apps, supported by all popular Python web
169
+ libraries. This is an advanced decorator that gives full flexibility in
170
+ defining one or more web endpoints on Modal.
171
+
172
+ **Usage:**
173
+
174
+ ```python
175
+ from typing import Callable
176
+
177
+ @app.function()
178
+ @modal.asgi_app()
179
+ def create_asgi() -> Callable:
180
+ ...
181
+ ```
182
+
183
+ To learn how to use Modal with popular web frameworks, see the
184
+ [guide on web endpoints](https://modal.com/docs/guide/webhooks).
185
+ """
186
+ ...
187
+
101
188
  def wsgi_app(
102
189
  _warn_parentheses_missing=None,
103
190
  *,
@@ -113,7 +200,30 @@ def wsgi_app(
113
200
  ]
114
201
  ],
115
202
  PartialFunction,
116
- ]: ...
203
+ ]:
204
+ """Decorator for registering a WSGI app with a Modal function.
205
+
206
+ Web Server Gateway Interface (WSGI) is a standard for synchronous Python web apps.
207
+ It has been [succeeded by the ASGI interface](https://asgi.readthedocs.io/en/latest/introduction.html#wsgi-compatibility)
208
+ which is compatible with ASGI and supports additional functionality such as web sockets.
209
+ Modal supports ASGI via [`asgi_app`](https://modal.com/docs/reference/modal.asgi_app).
210
+
211
+ **Usage:**
212
+
213
+ ```python
214
+ from typing import Callable
215
+
216
+ @app.function()
217
+ @modal.wsgi_app()
218
+ def create_wsgi() -> Callable:
219
+ ...
220
+ ```
221
+
222
+ To learn how to use this decorator with popular web frameworks, see the
223
+ [guide on web endpoints](https://modal.com/docs/guide/webhooks).
224
+ """
225
+ ...
226
+
117
227
  def web_server(
118
228
  port: int,
119
229
  *,
@@ -130,20 +240,84 @@ def web_server(
130
240
  ]
131
241
  ],
132
242
  PartialFunction,
133
- ]: ...
243
+ ]:
244
+ """Decorator that registers an HTTP web server inside the container.
245
+
246
+ This is similar to `@asgi_app` and `@wsgi_app`, but it allows you to expose a full HTTP server
247
+ listening on a container port. This is useful for servers written in other languages like Rust,
248
+ as well as integrating with non-ASGI frameworks like aiohttp and Tornado.
249
+
250
+ **Usage:**
251
+
252
+ ```python
253
+ import subprocess
254
+
255
+ @app.function()
256
+ @modal.web_server(8000)
257
+ def my_file_server():
258
+ subprocess.Popen("python -m http.server -d / 8000", shell=True)
259
+ ```
260
+
261
+ The above example starts a simple file server, displaying the contents of the root directory.
262
+ Here, requests to the web endpoint will go to external port 8000 on the container. The
263
+ `http.server` module is included with Python, but you could run anything here.
264
+
265
+ Internally, the web server is transparently converted into a web endpoint by Modal, so it has
266
+ the same serverless autoscaling behavior as other web endpoints.
267
+
268
+ For more info, see the [guide on web endpoints](https://modal.com/docs/guide/webhooks).
269
+ """
270
+ ...
271
+
134
272
  def build(
135
273
  _warn_parentheses_missing=None, *, force: bool = False, timeout: int = 86400
136
274
  ) -> collections.abc.Callable[
137
275
  [typing.Union[PartialFunction, collections.abc.Callable[[typing.Any], typing.Any]]], PartialFunction
138
- ]: ...
276
+ ]:
277
+ """mdmd:hidden
278
+ Decorator for methods that execute at _build time_ to create a new Image layer.
279
+
280
+ **Deprecated**: This function is deprecated. We recommend using `modal.Volume`
281
+ to store large assets (such as model weights) instead of writing them to the
282
+ Image during the build process. For other use cases, you can replace this
283
+ decorator with the `Image.run_function` method.
284
+
285
+ **Usage**
286
+
287
+ ```python notest
288
+ @app.cls(gpu="A10G")
289
+ class AlpacaLoRAModel:
290
+ @build()
291
+ def download_models(self):
292
+ model = LlamaForCausalLM.from_pretrained(
293
+ base_model,
294
+ )
295
+ PeftModel.from_pretrained(model, lora_weights)
296
+ LlamaTokenizer.from_pretrained(base_model)
297
+ ```
298
+ """
299
+ ...
300
+
139
301
  def enter(
140
302
  _warn_parentheses_missing=None, *, snap: bool = False
141
303
  ) -> collections.abc.Callable[
142
304
  [typing.Union[PartialFunction, collections.abc.Callable[[typing.Any], typing.Any]]], PartialFunction
143
- ]: ...
305
+ ]:
306
+ """Decorator for methods which should be executed when a new container is started.
307
+
308
+ See the [lifeycle function guide](https://modal.com/docs/guide/lifecycle-functions#enter) for more information.
309
+ """
310
+ ...
311
+
144
312
  def exit(
145
313
  _warn_parentheses_missing=None,
146
- ) -> collections.abc.Callable[[collections.abc.Callable[[typing.Any], typing.Any]], PartialFunction]: ...
314
+ ) -> collections.abc.Callable[[collections.abc.Callable[[typing.Any], typing.Any]], PartialFunction]:
315
+ """Decorator for methods which should be executed when a container is about to exit.
316
+
317
+ See the [lifeycle function guide](https://modal.com/docs/guide/lifecycle-functions#exit) for more information.
318
+ """
319
+ ...
320
+
147
321
  def batched(
148
322
  _warn_parentheses_missing=None, *, max_batch_size: int, wait_ms: int
149
323
  ) -> collections.abc.Callable[
@@ -156,7 +330,33 @@ def batched(
156
330
  ]
157
331
  ],
158
332
  PartialFunction[modal._partial_function.P, modal._partial_function.ReturnType, modal._partial_function.ReturnType],
159
- ]: ...
333
+ ]:
334
+ """Decorator for functions or class methods that should be batched.
335
+
336
+ **Usage**
337
+
338
+ ```python
339
+ # Stack the decorator under `@app.function()` to enable dynamic batching
340
+ @app.function()
341
+ @modal.batched(max_batch_size=4, wait_ms=1000)
342
+ async def batched_multiply(xs: list[int], ys: list[int]) -> list[int]:
343
+ return [x * y for x, y in zip(xs, ys)]
344
+
345
+ # call batched_multiply with individual inputs
346
+ # batched_multiply.remote.aio(2, 100)
347
+
348
+ # With `@app.cls()`, apply the decorator to a method (this may change in the future)
349
+ @app.cls()
350
+ class BatchedClass:
351
+ @modal.batched(max_batch_size=4, wait_ms=1000)
352
+ def batched_multiply(self, xs: list[int], ys: list[int]) -> list[int]:
353
+ return [x * y for x, y in zip(xs, ys)]
354
+ ```
355
+
356
+ See the [dynamic batching guide](https://modal.com/docs/guide/dynamic-batching) for more information.
357
+ """
358
+ ...
359
+
160
360
  def concurrent(
161
361
  _warn_parentheses_missing=None, *, max_inputs: int, target_inputs: typing.Optional[int] = None
162
362
  ) -> collections.abc.Callable[
@@ -169,4 +369,45 @@ def concurrent(
169
369
  ]
170
370
  ],
171
371
  PartialFunction[modal._partial_function.P, modal._partial_function.ReturnType, modal._partial_function.ReturnType],
172
- ]: ...
372
+ ]:
373
+ """Decorator that allows individual containers to handle multiple inputs concurrently.
374
+
375
+ The concurrency mechanism depends on whether the function is async or not:
376
+ - Async functions will run inputs on a single thread as asyncio tasks.
377
+ - Synchronous functions will use multi-threading. The code must be thread-safe.
378
+
379
+ Input concurrency will be most useful for workflows that are IO-bound
380
+ (e.g., making network requests) or when running an inference server that supports
381
+ dynamic batching.
382
+
383
+ When `target_inputs` is set, Modal's autoscaler will try to provision resources
384
+ such that each container is running that many inputs concurrently, rather than
385
+ autoscaling based on `max_inputs`. Containers may burst up to up to `max_inputs`
386
+ if resources are insufficient to remain at the target concurrency, e.g. when the
387
+ arrival rate of inputs increases. This can trade-off a small increase in average
388
+ latency to avoid larger tail latencies from input queuing.
389
+
390
+ **Examples:**
391
+ ```python
392
+ # Stack the decorator under `@app.function()` to enable input concurrency
393
+ @app.function()
394
+ @modal.concurrent(max_inputs=100)
395
+ async def f(data):
396
+ # Async function; will be scheduled as asyncio task
397
+ ...
398
+
399
+ # With `@app.cls()`, apply the decorator at the class level, not on individual methods
400
+ @app.cls()
401
+ @modal.concurrent(max_inputs=100, target_inputs=80)
402
+ class C:
403
+ @modal.method()
404
+ def f(self, data):
405
+ # Sync function; must be thread-safe
406
+ ...
407
+
408
+ ```
409
+
410
+ *Added in v0.73.148:* This decorator replaces the `allow_concurrent_inputs` parameter
411
+ in `@app.function()` and `@app.cls()`.
412
+ """
413
+ ...
modal/proxy.py CHANGED
@@ -12,7 +12,7 @@ class _Proxy(_Object, type_prefix="pr"):
12
12
  """Proxy objects give your Modal containers a static outbound IP address.
13
13
 
14
14
  This can be used for connecting to a remote address with network whitelist, for example
15
- a database. See [the guide](/docs/guide/proxy-ips) for more information.
15
+ a database. See [the guide](https://modal.com/docs/guide/proxy-ips) for more information.
16
16
  """
17
17
 
18
18
  @staticmethod
modal/proxy.pyi CHANGED
@@ -3,10 +3,35 @@ import modal.object
3
3
  import typing
4
4
 
5
5
  class _Proxy(modal._object._Object):
6
+ """Proxy objects give your Modal containers a static outbound IP address.
7
+
8
+ This can be used for connecting to a remote address with network whitelist, for example
9
+ a database. See [the guide](https://modal.com/docs/guide/proxy-ips) for more information.
10
+ """
6
11
  @staticmethod
7
- def from_name(name: str, *, environment_name: typing.Optional[str] = None) -> _Proxy: ...
12
+ def from_name(name: str, *, environment_name: typing.Optional[str] = None) -> _Proxy:
13
+ """Reference a Proxy by its name.
14
+
15
+ In contrast to most other Modal objects, new Proxy objects must be
16
+ provisioned via the Dashboard and cannot be created on the fly from code.
17
+ """
18
+ ...
8
19
 
9
20
  class Proxy(modal.object.Object):
10
- def __init__(self, *args, **kwargs): ...
21
+ """Proxy objects give your Modal containers a static outbound IP address.
22
+
23
+ This can be used for connecting to a remote address with network whitelist, for example
24
+ a database. See [the guide](https://modal.com/docs/guide/proxy-ips) for more information.
25
+ """
26
+ def __init__(self, *args, **kwargs):
27
+ """mdmd:hidden"""
28
+ ...
29
+
11
30
  @staticmethod
12
- def from_name(name: str, *, environment_name: typing.Optional[str] = None) -> Proxy: ...
31
+ def from_name(name: str, *, environment_name: typing.Optional[str] = None) -> Proxy:
32
+ """Reference a Proxy by its name.
33
+
34
+ In contrast to most other Modal objects, new Proxy objects must be
35
+ provisioned via the Dashboard and cannot be created on the fly from code.
36
+ """
37
+ ...
modal/queue.py CHANGED
@@ -64,7 +64,7 @@ class _Queue(_Object, type_prefix="qu"):
64
64
  assert queue.get() == 42
65
65
  ```
66
66
 
67
- For more examples, see the [guide](/docs/guide/dicts-and-queues#modal-queues).
67
+ For more examples, see the [guide](https://modal.com/docs/guide/dicts-and-queues#modal-queues).
68
68
 
69
69
  **Queue partitions (beta)**
70
70