modal 1.1.2.dev5__py3-none-any.whl → 1.1.2.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.
- modal/_runtime/container_io_manager.py +1 -5
- modal/client.pyi +2 -2
- modal/functions.pyi +6 -6
- modal/parallel_map.py +202 -101
- modal/parallel_map.pyi +63 -0
- {modal-1.1.2.dev5.dist-info → modal-1.1.2.dev7.dist-info}/METADATA +1 -1
- {modal-1.1.2.dev5.dist-info → modal-1.1.2.dev7.dist-info}/RECORD +12 -12
- modal_version/__init__.py +1 -1
- {modal-1.1.2.dev5.dist-info → modal-1.1.2.dev7.dist-info}/WHEEL +0 -0
- {modal-1.1.2.dev5.dist-info → modal-1.1.2.dev7.dist-info}/entry_points.txt +0 -0
- {modal-1.1.2.dev5.dist-info → modal-1.1.2.dev7.dist-info}/licenses/LICENSE +0 -0
- {modal-1.1.2.dev5.dist-info → modal-1.1.2.dev7.dist-info}/top_level.txt +0 -0
|
@@ -300,11 +300,7 @@ class _ContainerIOManager:
|
|
|
300
300
|
self.function_def = container_args.function_def
|
|
301
301
|
self.checkpoint_id = container_args.checkpoint_id or None
|
|
302
302
|
|
|
303
|
-
|
|
304
|
-
self.input_plane_server_url = None
|
|
305
|
-
for obj in container_args.app_layout.objects:
|
|
306
|
-
if obj.object_id == self.function_id:
|
|
307
|
-
self.input_plane_server_url = obj.function_handle_metadata.input_plane_url
|
|
303
|
+
self.input_plane_server_url = container_args.input_plane_server_url
|
|
308
304
|
|
|
309
305
|
self.calls_completed = 0
|
|
310
306
|
self.total_user_time = 0.0
|
modal/client.pyi
CHANGED
|
@@ -33,7 +33,7 @@ class _Client:
|
|
|
33
33
|
server_url: str,
|
|
34
34
|
client_type: int,
|
|
35
35
|
credentials: typing.Optional[tuple[str, str]],
|
|
36
|
-
version: str = "1.1.2.
|
|
36
|
+
version: str = "1.1.2.dev7",
|
|
37
37
|
):
|
|
38
38
|
"""mdmd:hidden
|
|
39
39
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -164,7 +164,7 @@ class Client:
|
|
|
164
164
|
server_url: str,
|
|
165
165
|
client_type: int,
|
|
166
166
|
credentials: typing.Optional[tuple[str, str]],
|
|
167
|
-
version: str = "1.1.2.
|
|
167
|
+
version: str = "1.1.2.dev7",
|
|
168
168
|
):
|
|
169
169
|
"""mdmd:hidden
|
|
170
170
|
The Modal client object is not intended to be instantiated directly by users.
|
modal/functions.pyi
CHANGED
|
@@ -427,7 +427,7 @@ class Function(
|
|
|
427
427
|
|
|
428
428
|
_call_generator: ___call_generator_spec[typing_extensions.Self]
|
|
429
429
|
|
|
430
|
-
class __remote_spec(typing_extensions.Protocol[
|
|
430
|
+
class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
|
|
431
431
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
|
|
432
432
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
433
433
|
...
|
|
@@ -436,7 +436,7 @@ class Function(
|
|
|
436
436
|
"""Calls the function remotely, executing it with the given arguments and returning the execution's result."""
|
|
437
437
|
...
|
|
438
438
|
|
|
439
|
-
remote: __remote_spec[modal._functions.
|
|
439
|
+
remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
|
|
440
440
|
|
|
441
441
|
class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
|
|
442
442
|
def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
|
|
@@ -463,7 +463,7 @@ class Function(
|
|
|
463
463
|
"""
|
|
464
464
|
...
|
|
465
465
|
|
|
466
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
|
466
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
|
|
467
467
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
468
468
|
"""[Experimental] Calls the function with the given arguments, without waiting for the results.
|
|
469
469
|
|
|
@@ -487,7 +487,7 @@ class Function(
|
|
|
487
487
|
...
|
|
488
488
|
|
|
489
489
|
_experimental_spawn: ___experimental_spawn_spec[
|
|
490
|
-
modal._functions.
|
|
490
|
+
modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
|
|
491
491
|
]
|
|
492
492
|
|
|
493
493
|
class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
|
|
@@ -496,7 +496,7 @@ class Function(
|
|
|
496
496
|
|
|
497
497
|
_spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
|
|
498
498
|
|
|
499
|
-
class __spawn_spec(typing_extensions.Protocol[
|
|
499
|
+
class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
|
|
500
500
|
def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
|
|
501
501
|
"""Calls the function with the given arguments, without waiting for the results.
|
|
502
502
|
|
|
@@ -517,7 +517,7 @@ class Function(
|
|
|
517
517
|
"""
|
|
518
518
|
...
|
|
519
519
|
|
|
520
|
-
spawn: __spawn_spec[modal._functions.
|
|
520
|
+
spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
|
|
521
521
|
|
|
522
522
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
|
|
523
523
|
"""Return the inner Python object wrapped by this Modal Function."""
|
modal/parallel_map.py
CHANGED
|
@@ -86,6 +86,180 @@ if typing.TYPE_CHECKING:
|
|
|
86
86
|
import modal.functions
|
|
87
87
|
|
|
88
88
|
|
|
89
|
+
class InputPreprocessor:
|
|
90
|
+
"""
|
|
91
|
+
Constructs FunctionPutInputsItem objects from the raw-input queue, and puts them in the processed-input queue.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
client: "modal.client._Client",
|
|
97
|
+
*,
|
|
98
|
+
raw_input_queue: _SynchronizedQueue,
|
|
99
|
+
processed_input_queue: asyncio.Queue,
|
|
100
|
+
function: "modal.functions._Function",
|
|
101
|
+
created_callback: Callable[[int], None],
|
|
102
|
+
done_callback: Callable[[], None],
|
|
103
|
+
):
|
|
104
|
+
self.client = client
|
|
105
|
+
self.function = function
|
|
106
|
+
self.inputs_created = 0
|
|
107
|
+
self.raw_input_queue = raw_input_queue
|
|
108
|
+
self.processed_input_queue = processed_input_queue
|
|
109
|
+
self.created_callback = created_callback
|
|
110
|
+
self.done_callback = done_callback
|
|
111
|
+
|
|
112
|
+
async def input_iter(self):
|
|
113
|
+
while 1:
|
|
114
|
+
raw_input = await self.raw_input_queue.get()
|
|
115
|
+
if raw_input is None: # end of input sentinel
|
|
116
|
+
break
|
|
117
|
+
yield raw_input # args, kwargs
|
|
118
|
+
|
|
119
|
+
def create_input_factory(self):
|
|
120
|
+
async def create_input(argskwargs):
|
|
121
|
+
idx = self.inputs_created
|
|
122
|
+
self.inputs_created += 1
|
|
123
|
+
self.created_callback(self.inputs_created)
|
|
124
|
+
(args, kwargs) = argskwargs
|
|
125
|
+
return await _create_input(
|
|
126
|
+
args,
|
|
127
|
+
kwargs,
|
|
128
|
+
self.client.stub,
|
|
129
|
+
max_object_size_bytes=self.function._max_object_size_bytes,
|
|
130
|
+
idx=idx,
|
|
131
|
+
method_name=self.function._use_method_name,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return create_input
|
|
135
|
+
|
|
136
|
+
async def drain_input_generator(self):
|
|
137
|
+
# Parallelize uploading blobs
|
|
138
|
+
async with aclosing(
|
|
139
|
+
async_map_ordered(self.input_iter(), self.create_input_factory(), concurrency=BLOB_MAX_PARALLELISM)
|
|
140
|
+
) as streamer:
|
|
141
|
+
async for item in streamer:
|
|
142
|
+
await self.processed_input_queue.put(item)
|
|
143
|
+
|
|
144
|
+
# close queue iterator
|
|
145
|
+
await self.processed_input_queue.put(None)
|
|
146
|
+
self.done_callback()
|
|
147
|
+
yield
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class InputPumper:
|
|
151
|
+
"""
|
|
152
|
+
Reads inputs from a queue of FunctionPutInputsItems, and sends them to the server.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
def __init__(
|
|
156
|
+
self,
|
|
157
|
+
client: "modal.client._Client",
|
|
158
|
+
*,
|
|
159
|
+
input_queue: asyncio.Queue,
|
|
160
|
+
function: "modal.functions._Function",
|
|
161
|
+
function_call_id: str,
|
|
162
|
+
map_items_manager: Optional["_MapItemsManager"] = None,
|
|
163
|
+
):
|
|
164
|
+
self.client = client
|
|
165
|
+
self.function = function
|
|
166
|
+
self.map_items_manager = map_items_manager
|
|
167
|
+
self.input_queue = input_queue
|
|
168
|
+
self.inputs_sent = 0
|
|
169
|
+
self.function_call_id = function_call_id
|
|
170
|
+
|
|
171
|
+
async def pump_inputs(self):
|
|
172
|
+
assert self.client.stub
|
|
173
|
+
async for items in queue_batch_iterator(self.input_queue, max_batch_size=MAP_INVOCATION_CHUNK_SIZE):
|
|
174
|
+
# Add items to the manager. Their state will be SENDING.
|
|
175
|
+
if self.map_items_manager is not None:
|
|
176
|
+
await self.map_items_manager.add_items(items)
|
|
177
|
+
request = api_pb2.FunctionPutInputsRequest(
|
|
178
|
+
function_id=self.function.object_id,
|
|
179
|
+
inputs=items,
|
|
180
|
+
function_call_id=self.function_call_id,
|
|
181
|
+
)
|
|
182
|
+
logger.debug(
|
|
183
|
+
f"Pushing {len(items)} inputs to server. Num queued inputs awaiting"
|
|
184
|
+
f" push is {self.input_queue.qsize()}. "
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
resp = await self._send_inputs(self.client.stub.FunctionPutInputs, request)
|
|
188
|
+
self.inputs_sent += len(items)
|
|
189
|
+
# Change item state to WAITING_FOR_OUTPUT, and set the input_id and input_jwt which are in the response.
|
|
190
|
+
if self.map_items_manager is not None:
|
|
191
|
+
self.map_items_manager.handle_put_inputs_response(resp.inputs)
|
|
192
|
+
logger.debug(
|
|
193
|
+
f"Successfully pushed {len(items)} inputs to server. "
|
|
194
|
+
f"Num queued inputs awaiting push is {self.input_queue.qsize()}."
|
|
195
|
+
)
|
|
196
|
+
yield
|
|
197
|
+
|
|
198
|
+
async def _send_inputs(
|
|
199
|
+
self,
|
|
200
|
+
fn: "modal.client.UnaryUnaryWrapper",
|
|
201
|
+
request: typing.Union[api_pb2.FunctionPutInputsRequest, api_pb2.FunctionRetryInputsRequest],
|
|
202
|
+
) -> typing.Union[api_pb2.FunctionPutInputsResponse, api_pb2.FunctionRetryInputsResponse]:
|
|
203
|
+
# with 8 retries we log the warning below about every 30 seconds which isn't too spammy.
|
|
204
|
+
retry_warning_message = RetryWarningMessage(
|
|
205
|
+
message=f"Warning: map progress for function {self.function._function_name} is limited."
|
|
206
|
+
" Common bottlenecks include slow iteration over results, or function backlogs.",
|
|
207
|
+
warning_interval=8,
|
|
208
|
+
errors_to_warn_for=[Status.RESOURCE_EXHAUSTED],
|
|
209
|
+
)
|
|
210
|
+
return await retry_transient_errors(
|
|
211
|
+
fn,
|
|
212
|
+
request,
|
|
213
|
+
max_retries=None,
|
|
214
|
+
max_delay=PUMP_INPUTS_MAX_RETRY_DELAY,
|
|
215
|
+
additional_status_codes=[Status.RESOURCE_EXHAUSTED],
|
|
216
|
+
retry_warning_message=retry_warning_message,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class SyncInputPumper(InputPumper):
|
|
221
|
+
def __init__(
|
|
222
|
+
self,
|
|
223
|
+
client: "modal.client._Client",
|
|
224
|
+
*,
|
|
225
|
+
input_queue: asyncio.Queue,
|
|
226
|
+
retry_queue: TimestampPriorityQueue,
|
|
227
|
+
function: "modal.functions._Function",
|
|
228
|
+
function_call_jwt: str,
|
|
229
|
+
function_call_id: str,
|
|
230
|
+
map_items_manager: "_MapItemsManager",
|
|
231
|
+
):
|
|
232
|
+
super().__init__(
|
|
233
|
+
client,
|
|
234
|
+
input_queue=input_queue,
|
|
235
|
+
function=function,
|
|
236
|
+
function_call_id=function_call_id,
|
|
237
|
+
map_items_manager=map_items_manager,
|
|
238
|
+
)
|
|
239
|
+
self.retry_queue = retry_queue
|
|
240
|
+
self.inputs_retried = 0
|
|
241
|
+
self.function_call_jwt = function_call_jwt
|
|
242
|
+
|
|
243
|
+
async def retry_inputs(self):
|
|
244
|
+
async for retriable_idxs in queue_batch_iterator(self.retry_queue, max_batch_size=MAP_INVOCATION_CHUNK_SIZE):
|
|
245
|
+
# For each index, use the context in the manager to create a FunctionRetryInputsItem.
|
|
246
|
+
# This will also update the context state to RETRYING.
|
|
247
|
+
inputs: list[api_pb2.FunctionRetryInputsItem] = await self.map_items_manager.prepare_items_for_retry(
|
|
248
|
+
retriable_idxs
|
|
249
|
+
)
|
|
250
|
+
request = api_pb2.FunctionRetryInputsRequest(
|
|
251
|
+
function_call_jwt=self.function_call_jwt,
|
|
252
|
+
inputs=inputs,
|
|
253
|
+
)
|
|
254
|
+
resp = await self._send_inputs(self.client.stub.FunctionRetryInputs, request)
|
|
255
|
+
# Update the state to WAITING_FOR_OUTPUT, and update the input_jwt in the context
|
|
256
|
+
# to the new value in the response.
|
|
257
|
+
self.map_items_manager.handle_retry_response(resp.input_jwts)
|
|
258
|
+
logger.debug(f"Successfully pushed retry for {len(inputs)} to server.")
|
|
259
|
+
self.inputs_retried += len(inputs)
|
|
260
|
+
yield
|
|
261
|
+
|
|
262
|
+
|
|
89
263
|
async def _map_invocation(
|
|
90
264
|
function: "modal.functions._Function",
|
|
91
265
|
raw_input_queue: _SynchronizedQueue,
|
|
@@ -117,8 +291,6 @@ async def _map_invocation(
|
|
|
117
291
|
have_all_inputs = False
|
|
118
292
|
map_done_event = asyncio.Event()
|
|
119
293
|
inputs_created = 0
|
|
120
|
-
inputs_sent = 0
|
|
121
|
-
inputs_retried = 0
|
|
122
294
|
outputs_completed = 0
|
|
123
295
|
outputs_received = 0
|
|
124
296
|
retried_outputs = 0
|
|
@@ -135,25 +307,24 @@ async def _map_invocation(
|
|
|
135
307
|
retry_policy, function_call_invocation_type, retry_queue, sync_client_retries_enabled, max_inputs_outstanding
|
|
136
308
|
)
|
|
137
309
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
max_object_size_bytes=function._max_object_size_bytes,
|
|
147
|
-
idx=idx,
|
|
148
|
-
method_name=function._use_method_name,
|
|
149
|
-
)
|
|
310
|
+
input_preprocessor = InputPreprocessor(
|
|
311
|
+
client=client,
|
|
312
|
+
raw_input_queue=raw_input_queue,
|
|
313
|
+
processed_input_queue=input_queue,
|
|
314
|
+
function=function,
|
|
315
|
+
created_callback=lambda x: update_state(set_inputs_created=x),
|
|
316
|
+
done_callback=lambda: update_state(set_have_all_inputs=True),
|
|
317
|
+
)
|
|
150
318
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
319
|
+
input_pumper = SyncInputPumper(
|
|
320
|
+
client=client,
|
|
321
|
+
input_queue=input_queue,
|
|
322
|
+
retry_queue=retry_queue,
|
|
323
|
+
function=function,
|
|
324
|
+
map_items_manager=map_items_manager,
|
|
325
|
+
function_call_jwt=function_call_jwt,
|
|
326
|
+
function_call_id=function_call_id,
|
|
327
|
+
)
|
|
157
328
|
|
|
158
329
|
def update_state(set_have_all_inputs=None, set_inputs_created=None, set_outputs_completed=None):
|
|
159
330
|
# This should be the only method that needs nonlocal of the following vars
|
|
@@ -175,84 +346,6 @@ async def _map_invocation(
|
|
|
175
346
|
# map is done
|
|
176
347
|
map_done_event.set()
|
|
177
348
|
|
|
178
|
-
async def drain_input_generator():
|
|
179
|
-
# Parallelize uploading blobs
|
|
180
|
-
async with aclosing(
|
|
181
|
-
async_map_ordered(input_iter(), create_input, concurrency=BLOB_MAX_PARALLELISM)
|
|
182
|
-
) as streamer:
|
|
183
|
-
async for item in streamer:
|
|
184
|
-
await input_queue.put(item)
|
|
185
|
-
|
|
186
|
-
# close queue iterator
|
|
187
|
-
await input_queue.put(None)
|
|
188
|
-
update_state(set_have_all_inputs=True)
|
|
189
|
-
yield
|
|
190
|
-
|
|
191
|
-
async def pump_inputs():
|
|
192
|
-
assert client.stub
|
|
193
|
-
nonlocal inputs_sent
|
|
194
|
-
async for items in queue_batch_iterator(input_queue, max_batch_size=MAP_INVOCATION_CHUNK_SIZE):
|
|
195
|
-
# Add items to the manager. Their state will be SENDING.
|
|
196
|
-
await map_items_manager.add_items(items)
|
|
197
|
-
request = api_pb2.FunctionPutInputsRequest(
|
|
198
|
-
function_id=function.object_id,
|
|
199
|
-
inputs=items,
|
|
200
|
-
function_call_id=function_call_id,
|
|
201
|
-
)
|
|
202
|
-
logger.debug(
|
|
203
|
-
f"Pushing {len(items)} inputs to server. Num queued inputs awaiting push is {input_queue.qsize()}."
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
resp = await send_inputs(client.stub.FunctionPutInputs, request)
|
|
207
|
-
inputs_sent += len(items)
|
|
208
|
-
# Change item state to WAITING_FOR_OUTPUT, and set the input_id and input_jwt which are in the response.
|
|
209
|
-
map_items_manager.handle_put_inputs_response(resp.inputs)
|
|
210
|
-
logger.debug(
|
|
211
|
-
f"Successfully pushed {len(items)} inputs to server. "
|
|
212
|
-
f"Num queued inputs awaiting push is {input_queue.qsize()}."
|
|
213
|
-
)
|
|
214
|
-
yield
|
|
215
|
-
|
|
216
|
-
async def retry_inputs():
|
|
217
|
-
nonlocal inputs_retried
|
|
218
|
-
async for retriable_idxs in queue_batch_iterator(retry_queue, max_batch_size=MAP_INVOCATION_CHUNK_SIZE):
|
|
219
|
-
# For each index, use the context in the manager to create a FunctionRetryInputsItem.
|
|
220
|
-
# This will also update the context state to RETRYING.
|
|
221
|
-
inputs: list[api_pb2.FunctionRetryInputsItem] = await map_items_manager.prepare_items_for_retry(
|
|
222
|
-
retriable_idxs
|
|
223
|
-
)
|
|
224
|
-
request = api_pb2.FunctionRetryInputsRequest(
|
|
225
|
-
function_call_jwt=function_call_jwt,
|
|
226
|
-
inputs=inputs,
|
|
227
|
-
)
|
|
228
|
-
resp = await send_inputs(client.stub.FunctionRetryInputs, request)
|
|
229
|
-
# Update the state to WAITING_FOR_OUTPUT, and update the input_jwt in the context
|
|
230
|
-
# to the new value in the response.
|
|
231
|
-
map_items_manager.handle_retry_response(resp.input_jwts)
|
|
232
|
-
logger.debug(f"Successfully pushed retry for {len(inputs)} to server.")
|
|
233
|
-
inputs_retried += len(inputs)
|
|
234
|
-
yield
|
|
235
|
-
|
|
236
|
-
async def send_inputs(
|
|
237
|
-
fn: "modal.client.UnaryUnaryWrapper",
|
|
238
|
-
request: typing.Union[api_pb2.FunctionPutInputsRequest, api_pb2.FunctionRetryInputsRequest],
|
|
239
|
-
) -> typing.Union[api_pb2.FunctionPutInputsResponse, api_pb2.FunctionRetryInputsResponse]:
|
|
240
|
-
# with 8 retries we log the warning below about every 30 seconds which isn't too spammy.
|
|
241
|
-
retry_warning_message = RetryWarningMessage(
|
|
242
|
-
message=f"Warning: map progress for function {function._function_name} is limited."
|
|
243
|
-
" Common bottlenecks include slow iteration over results, or function backlogs.",
|
|
244
|
-
warning_interval=8,
|
|
245
|
-
errors_to_warn_for=[Status.RESOURCE_EXHAUSTED],
|
|
246
|
-
)
|
|
247
|
-
return await retry_transient_errors(
|
|
248
|
-
fn,
|
|
249
|
-
request,
|
|
250
|
-
max_retries=None,
|
|
251
|
-
max_delay=PUMP_INPUTS_MAX_RETRY_DELAY,
|
|
252
|
-
additional_status_codes=[Status.RESOURCE_EXHAUSTED],
|
|
253
|
-
retry_warning_message=retry_warning_message,
|
|
254
|
-
)
|
|
255
|
-
|
|
256
349
|
async def get_all_outputs():
|
|
257
350
|
assert client.stub
|
|
258
351
|
nonlocal \
|
|
@@ -395,8 +488,11 @@ async def _map_invocation(
|
|
|
395
488
|
def log_stats():
|
|
396
489
|
logger.debug(
|
|
397
490
|
f"Map stats: sync_client_retries_enabled={sync_client_retries_enabled} "
|
|
398
|
-
f"have_all_inputs={have_all_inputs}
|
|
399
|
-
f"
|
|
491
|
+
f"have_all_inputs={have_all_inputs} "
|
|
492
|
+
f"inputs_created={inputs_created} "
|
|
493
|
+
f"input_sent={input_pumper.inputs_sent} "
|
|
494
|
+
f"inputs_retried={input_pumper.inputs_retried} "
|
|
495
|
+
f"outputs_received={outputs_received} "
|
|
400
496
|
f"successful_completions={successful_completions} failed_completions={failed_completions} "
|
|
401
497
|
f"no_context_duplicates={no_context_duplicates} old_retry_duplicates={stale_retry_duplicates} "
|
|
402
498
|
f"already_complete_duplicates={already_complete_duplicates} "
|
|
@@ -415,7 +511,12 @@ async def _map_invocation(
|
|
|
415
511
|
|
|
416
512
|
log_debug_stats_task = asyncio.create_task(log_debug_stats())
|
|
417
513
|
async with aclosing(
|
|
418
|
-
async_merge(
|
|
514
|
+
async_merge(
|
|
515
|
+
input_preprocessor.drain_input_generator(),
|
|
516
|
+
input_pumper.pump_inputs(),
|
|
517
|
+
input_pumper.retry_inputs(),
|
|
518
|
+
poll_outputs(),
|
|
519
|
+
)
|
|
419
520
|
) as streamer:
|
|
420
521
|
async for response in streamer:
|
|
421
522
|
if response is not None: # type: ignore[unreachable]
|
modal/parallel_map.pyi
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import asyncio.events
|
|
3
|
+
import asyncio.queues
|
|
3
4
|
import collections.abc
|
|
4
5
|
import enum
|
|
5
6
|
import modal._functions
|
|
@@ -60,6 +61,68 @@ class _OutputValue:
|
|
|
60
61
|
"""Return self==value."""
|
|
61
62
|
...
|
|
62
63
|
|
|
64
|
+
class InputPreprocessor:
|
|
65
|
+
"""Constructs FunctionPutInputsItem objects from the raw-input queue, and puts them in the processed-input queue."""
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
client: modal.client._Client,
|
|
69
|
+
*,
|
|
70
|
+
raw_input_queue: _SynchronizedQueue,
|
|
71
|
+
processed_input_queue: asyncio.queues.Queue,
|
|
72
|
+
function: modal._functions._Function,
|
|
73
|
+
created_callback: collections.abc.Callable[[int], None],
|
|
74
|
+
done_callback: collections.abc.Callable[[], None],
|
|
75
|
+
):
|
|
76
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
def input_iter(self): ...
|
|
80
|
+
def create_input_factory(self): ...
|
|
81
|
+
def drain_input_generator(self): ...
|
|
82
|
+
|
|
83
|
+
class InputPumper:
|
|
84
|
+
"""Reads inputs from a queue of FunctionPutInputsItems, and sends them to the server."""
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
client: modal.client._Client,
|
|
88
|
+
*,
|
|
89
|
+
input_queue: asyncio.queues.Queue,
|
|
90
|
+
function: modal._functions._Function,
|
|
91
|
+
function_call_id: str,
|
|
92
|
+
map_items_manager: typing.Optional[_MapItemsManager] = None,
|
|
93
|
+
):
|
|
94
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
95
|
+
...
|
|
96
|
+
|
|
97
|
+
def pump_inputs(self): ...
|
|
98
|
+
async def _send_inputs(
|
|
99
|
+
self,
|
|
100
|
+
fn: modal.client.UnaryUnaryWrapper,
|
|
101
|
+
request: typing.Union[
|
|
102
|
+
modal_proto.api_pb2.FunctionPutInputsRequest, modal_proto.api_pb2.FunctionRetryInputsRequest
|
|
103
|
+
],
|
|
104
|
+
) -> typing.Union[
|
|
105
|
+
modal_proto.api_pb2.FunctionPutInputsResponse, modal_proto.api_pb2.FunctionRetryInputsResponse
|
|
106
|
+
]: ...
|
|
107
|
+
|
|
108
|
+
class SyncInputPumper(InputPumper):
|
|
109
|
+
"""Reads inputs from a queue of FunctionPutInputsItems, and sends them to the server."""
|
|
110
|
+
def __init__(
|
|
111
|
+
self,
|
|
112
|
+
client: modal.client._Client,
|
|
113
|
+
*,
|
|
114
|
+
input_queue: asyncio.queues.Queue,
|
|
115
|
+
retry_queue: modal._utils.async_utils.TimestampPriorityQueue,
|
|
116
|
+
function: modal._functions._Function,
|
|
117
|
+
function_call_jwt: str,
|
|
118
|
+
function_call_id: str,
|
|
119
|
+
map_items_manager: _MapItemsManager,
|
|
120
|
+
):
|
|
121
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
122
|
+
...
|
|
123
|
+
|
|
124
|
+
def retry_inputs(self): ...
|
|
125
|
+
|
|
63
126
|
def _map_invocation(
|
|
64
127
|
function: modal._functions._Function,
|
|
65
128
|
raw_input_queue: _SynchronizedQueue,
|
|
@@ -22,7 +22,7 @@ modal/app.py,sha256=kpq4kXp7pch688y6g55QYAC10wqPTU5FXKoWPMirA3E,47899
|
|
|
22
22
|
modal/app.pyi,sha256=-jKXlGDBWRPVsuenBhdMRqawK-L2eiJ7gHbmSblhltg,43525
|
|
23
23
|
modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
|
|
24
24
|
modal/client.py,sha256=kyAIVB3Ay-XKJizQ_1ufUFB__EagV0MLmHJpyYyJ7J0,18636
|
|
25
|
-
modal/client.pyi,sha256=
|
|
25
|
+
modal/client.pyi,sha256=XkYAet9eGfbO6wW4bcZfOEgO14pM4jUoNrsdUQ9mOFw,15829
|
|
26
26
|
modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
|
|
27
27
|
modal/cloud_bucket_mount.pyi,sha256=-qSfYAQvIoO_l2wsCCGTG5ZUwQieNKXdAO00yP1-LYU,7394
|
|
28
28
|
modal/cls.py,sha256=7A0xGnugQzm8dOfnKMjLjtqekRlRtQ0jPFRYgq6xdUM,40018
|
|
@@ -39,7 +39,7 @@ modal/file_io.py,sha256=BVqAJ0sgPUfN8QsYztWiGB4j56he60TncM02KsylnCw,21449
|
|
|
39
39
|
modal/file_io.pyi,sha256=cPT_hsplE5iLCXhYOLn1Sp9eDdk7DxdFmicQHanJZyg,15918
|
|
40
40
|
modal/file_pattern_matcher.py,sha256=A_Kdkej6q7YQyhM_2-BvpFmPqJ0oHb54B6yf9VqvPVE,8116
|
|
41
41
|
modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
|
|
42
|
-
modal/functions.pyi,sha256=
|
|
42
|
+
modal/functions.pyi,sha256=cS7QYPHcFD_p95hrAU-TWxBIraIzzG-uO9KyPAwfxdw,34766
|
|
43
43
|
modal/gpu.py,sha256=Fe5ORvVPDIstSq1xjmM6OoNgLYFWvogP9r5BgmD3hYg,6769
|
|
44
44
|
modal/image.py,sha256=A83nmo0zfCUwgvJh0LZ7Yc1sYvDnZLl_phbKxN-9HIw,103144
|
|
45
45
|
modal/image.pyi,sha256=oH-GCHVEwD5fOX0K_IaWN5RKZlYwX82z-K4wxx8aN3c,68541
|
|
@@ -52,8 +52,8 @@ modal/network_file_system.pyi,sha256=Td_IobHr84iLo_9LZKQ4tNdUB60yjX8QWBaFiUvhfi8
|
|
|
52
52
|
modal/object.py,sha256=bTeskuY8JFrESjU4_UL_nTwYlBQdOLmVaOX3X6EMxsg,164
|
|
53
53
|
modal/object.pyi,sha256=sgbaq_d3QSmnPKg5jRbMG3dOceKs0l54kHUAhAyZKAE,6796
|
|
54
54
|
modal/output.py,sha256=q4T9uHduunj4NwY-YSwkHGgjZlCXMuJbfQ5UFaAGRAc,1968
|
|
55
|
-
modal/parallel_map.py,sha256=
|
|
56
|
-
modal/parallel_map.pyi,sha256=
|
|
55
|
+
modal/parallel_map.py,sha256=Uvc8r9QJbzBlyDc7lhYG4Ul5VPnceGl73Y0CJJfeD54,62556
|
|
56
|
+
modal/parallel_map.pyi,sha256=Z-nIQDFiQMEABr5wNy3HH4K6DIr0LcnxrihF9kqdHa0,13938
|
|
57
57
|
modal/partial_function.py,sha256=aIdlGfTjjgqY6Fpr-biCjvRU9W542_S5N2xkNN_rYGM,1127
|
|
58
58
|
modal/partial_function.pyi,sha256=lqqOzZ9-QvHTDWKQ_oAYYOvsXgTOBKhO9u-RI98JbUk,13986
|
|
59
59
|
modal/proxy.py,sha256=NQJJMGo-D2IfmeU0vb10WWaE4oTLcuf9jTeEJvactOg,1446
|
|
@@ -82,7 +82,7 @@ modal/volume.py,sha256=784Wz4XeLYPGWChfpRbukvGK2Ujazf7-lHwDxUNoKfY,45424
|
|
|
82
82
|
modal/volume.pyi,sha256=YIzDSxDxy_pNrcWWX0cpAoNkFWwC-kzSnQQAaGeSW0Y,41951
|
|
83
83
|
modal/_runtime/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
|
84
84
|
modal/_runtime/asgi.py,sha256=_2xSTsDD27Cit7xnMs4lzkJA2wzer2_N4Oa3BkXFzVA,22521
|
|
85
|
-
modal/_runtime/container_io_manager.py,sha256=
|
|
85
|
+
modal/_runtime/container_io_manager.py,sha256=ujNpa8bLFM3krzWID8dLDjhEHNcd__Yb3b6XqeDRe8g,45237
|
|
86
86
|
modal/_runtime/container_io_manager.pyi,sha256=_HvYZzpXX-msFDFuOvk1z6L5DBbv5Dfly16PgYDOojY,23065
|
|
87
87
|
modal/_runtime/execution_context.py,sha256=73Y5zH_o-MhVCrkJXakYVlFkKqCa2CWvqoHjOfJrJGg,3034
|
|
88
88
|
modal/_runtime/execution_context.pyi,sha256=IFcW1jphqTchX4fy-45rqfz91RhkZPWtIhIvLvGsNGM,2294
|
|
@@ -151,7 +151,7 @@ modal/experimental/__init__.py,sha256=nuc7AL4r_Fs08DD5dciWFZhrV1nanwoClOfdTcudU0
|
|
|
151
151
|
modal/experimental/flash.py,sha256=viXQumCIFp5VFsPFURdFTBTjP_QnsAi8nSWXAMmfjeQ,19744
|
|
152
152
|
modal/experimental/flash.pyi,sha256=A8_qJGtGoXEzKDdHbvhmCw7oqfneFEvJQK3ZdTOvUdU,10830
|
|
153
153
|
modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
|
|
154
|
-
modal-1.1.2.
|
|
154
|
+
modal-1.1.2.dev7.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
|
155
155
|
modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
|
|
156
156
|
modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
|
|
157
157
|
modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
|
|
@@ -174,10 +174,10 @@ modal_proto/options_pb2.pyi,sha256=l7DBrbLO7q3Ir-XDkWsajm0d0TQqqrfuX54i4BMpdQg,1
|
|
|
174
174
|
modal_proto/options_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
175
175
|
modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0yJSI,247
|
|
176
176
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
177
|
-
modal_version/__init__.py,sha256=
|
|
177
|
+
modal_version/__init__.py,sha256=FBVqTKrDDKpzKSeuzdoOpG8l9rtT4-DQXthg9YcqBS4,120
|
|
178
178
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
|
179
|
-
modal-1.1.2.
|
|
180
|
-
modal-1.1.2.
|
|
181
|
-
modal-1.1.2.
|
|
182
|
-
modal-1.1.2.
|
|
183
|
-
modal-1.1.2.
|
|
179
|
+
modal-1.1.2.dev7.dist-info/METADATA,sha256=YymEqhZHcg5dEYtMpAzhLjhPWHckBw8bnJM6g5Zo-i4,2459
|
|
180
|
+
modal-1.1.2.dev7.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
|
181
|
+
modal-1.1.2.dev7.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
|
182
|
+
modal-1.1.2.dev7.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
|
|
183
|
+
modal-1.1.2.dev7.dist-info/RECORD,,
|
modal_version/__init__.py
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|