modal 0.73.158__py3-none-any.whl → 0.73.160__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.
@@ -2,7 +2,12 @@
2
2
  # ruff: noqa: E402
3
3
  import os
4
4
 
5
- from modal._runtime.user_code_imports import Service, import_class_service, import_single_function_service
5
+ from modal._runtime.user_code_imports import (
6
+ Service,
7
+ get_active_app_fallback,
8
+ import_class_service,
9
+ import_single_function_service,
10
+ )
6
11
 
7
12
  telemetry_socket = os.environ.get("MODAL_TELEMETRY_SOCKET")
8
13
  if telemetry_socket:
@@ -349,32 +354,6 @@ def call_function(
349
354
  signal.signal(signal.SIGUSR1, usr1_handler) # reset signal handler
350
355
 
351
356
 
352
- def get_active_app_fallback(function_def: api_pb2.Function) -> _App:
353
- # This branch is reached in the special case that the imported function/class is:
354
- # 1) not serialized, and
355
- # 2) isn't a FunctionHandle - i.e, not decorated at definition time
356
- # Look at all instantiated apps - if there is only one with the indicated name, use that one
357
- app_name: Optional[str] = function_def.app_name or None # coalesce protobuf field to None
358
- matching_apps = _App._all_apps.get(app_name, [])
359
- if len(matching_apps) == 1:
360
- active_app: _App = matching_apps[0]
361
- return active_app
362
-
363
- if len(matching_apps) > 1:
364
- if app_name is not None:
365
- warning_sub_message = f"app with the same name ('{app_name}')"
366
- else:
367
- warning_sub_message = "unnamed app"
368
- logger.warning(
369
- f"You have more than one {warning_sub_message}. "
370
- "It's recommended to name all your Apps uniquely when using multiple apps"
371
- )
372
-
373
- # If we don't have an active app, create one on the fly
374
- # The app object is used to carry the app layout etc
375
- return _App()
376
-
377
-
378
357
  def call_lifecycle_functions(
379
358
  event_loop: UserCodeEventLoop,
380
359
  container_io_manager, #: ContainerIOManager, TODO: this type is generated at runtime
@@ -416,9 +395,9 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
416
395
  with container_io_manager.heartbeats(is_snapshotting_function), UserCodeEventLoop() as event_loop:
417
396
  # If this is a serialized function, fetch the definition from the server
418
397
  if function_def.definition_type == api_pb2.Function.DEFINITION_TYPE_SERIALIZED:
419
- ser_cls, ser_fun = container_io_manager.get_serialized_function()
398
+ ser_usr_cls, ser_fun = container_io_manager.get_serialized_function()
420
399
  else:
421
- ser_cls, ser_fun = None, None
400
+ ser_usr_cls, ser_fun = None, None
422
401
 
423
402
  # Initialize the function, importing user code.
424
403
  with container_io_manager.handle_user_exception():
@@ -429,16 +408,28 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
429
408
  param_kwargs = {}
430
409
 
431
410
  if function_def.is_class:
411
+ # this is a bit ugly - match the function and class based on function name to get metadata
412
+ # This metadata is required in order to hydrate the class in case it's not globally
413
+ # decorated (or serialized)
414
+ service_base_function_id = container_args.app_layout.function_ids[function_def.function_name]
415
+ service_function_hydration_data = [
416
+ o for o in container_args.app_layout.objects if o.object_id == service_base_function_id
417
+ ][0]
418
+ class_id = container_args.app_layout.class_ids[function_def.function_name.removesuffix(".*")]
419
+
432
420
  service = import_class_service(
433
421
  function_def,
434
- ser_cls,
422
+ service_function_hydration_data,
423
+ class_id,
424
+ client,
425
+ ser_usr_cls,
435
426
  param_args,
436
427
  param_kwargs,
437
428
  )
438
429
  else:
439
430
  service = import_single_function_service(
440
431
  function_def,
441
- ser_cls,
432
+ ser_usr_cls,
442
433
  ser_fun,
443
434
  param_args,
444
435
  param_kwargs,
modal/_runtime/asgi.py CHANGED
@@ -473,7 +473,7 @@ async def _proxy_lifespan_request(base_url, scope, receive, send) -> None:
473
473
  read_bufsize=1024 * 1024, # 1 MiB
474
474
  connector=aiohttp.TCPConnector(
475
475
  limit=1000
476
- ), # 100 is the default max, but 1000 is the max for `allow_concurrent_inputs`.
476
+ ), # 100 is the default max, but 1000 is the max for `@modal.concurrent`.
477
477
  # Note: these values will need to be kept in sync.
478
478
  **(
479
479
  # These options were introduced in aiohttp 3.9, and we can remove the
@@ -10,9 +10,10 @@ import modal._runtime.container_io_manager
10
10
  import modal.cls
11
11
  from modal import Function
12
12
  from modal._functions import _Function
13
- from modal._partial_function import _find_partial_methods_for_user_cls, _PartialFunctionFlags
14
13
  from modal._utils.async_utils import synchronizer
15
14
  from modal._utils.function_utils import LocalFunctionError, is_async as get_is_async, is_global_object
15
+ from modal.app import _App
16
+ from modal.config import logger
16
17
  from modal.exception import ExecutionError, InvalidError
17
18
  from modal_proto import api_pb2
18
19
 
@@ -144,14 +145,13 @@ class ImportedClass(Service):
144
145
  self, fun_def: api_pb2.Function, container_io_manager: "modal._runtime.container_io_manager.ContainerIOManager"
145
146
  ) -> dict[str, "FinalizedFunction"]:
146
147
  finalized_functions = {}
147
- for method_name, partial in self._partial_functions.items():
148
- partial = synchronizer._translate_in(partial) # ugly
149
- user_func = partial.raw_f
148
+ for method_name, _partial in self._partial_functions.items():
149
+ user_func = _partial.raw_f
150
150
  # Check this property before we turn it into a method (overriden by webhooks)
151
151
  is_async = get_is_async(user_func)
152
152
  # Use the function definition for whether this is a generator (overriden by webhooks)
153
- is_generator = partial.params.is_generator
154
- webhook_config = partial.params.webhook_config
153
+ is_generator = _partial.params.is_generator
154
+ webhook_config = _partial.params.webhook_config
155
155
 
156
156
  bound_func = user_func.__get__(self.user_cls_instance)
157
157
 
@@ -178,22 +178,20 @@ class ImportedClass(Service):
178
178
  return finalized_functions
179
179
 
180
180
 
181
- def get_user_class_instance(cls: typing.Union[type, modal.cls.Cls], args: tuple, kwargs: dict[str, Any]) -> typing.Any:
181
+ def get_user_class_instance(_cls: modal.cls._Cls, args: tuple, kwargs: dict[str, Any]) -> typing.Any:
182
182
  """Returns instance of the underlying class to be used as the `self`
183
183
 
184
- The input `cls` can either be the raw Python class the user has declared ("user class"),
185
- or an @app.cls-decorated version of it which is a modal.Cls-instance wrapping the user class.
186
- """
187
- if isinstance(cls, modal.cls.Cls):
188
- # globally @app.cls-decorated class
189
- modal_obj: modal.cls.Obj = cls(*args, **kwargs)
190
- modal_obj._entered = True # ugly but prevents .local() from triggering additional enter-logic
191
- # TODO: unify lifecycle logic between .local() and container_entrypoint
192
- user_cls_instance = modal_obj._cached_user_cls_instance()
193
- else:
194
- # undecorated class (non-global decoration or serialized)
195
- user_cls_instance = cls(*args, **kwargs)
184
+ For the time being, this is an instance of the underlying user defined type, with
185
+ some extra attributes like parameter values and _modal_functions set, allowing
186
+ its methods to be used as modal Function objects with .remote() and .local() etc.
196
187
 
188
+ TODO: Could possibly change this to use an Obj to clean up the data model? would invalidate isinstance checks though
189
+ """
190
+ cls = synchronizer._translate_out(_cls) # ugly
191
+ modal_obj: modal.cls.Obj = cls(*args, **kwargs)
192
+ modal_obj._entered = True # ugly but prevents .local() from triggering additional enter-logic
193
+ # TODO: unify lifecycle logic between .local() and container_entrypoint
194
+ user_cls_instance = modal_obj._cached_user_cls_instance()
197
195
  return user_cls_instance
198
196
 
199
197
 
@@ -293,7 +291,10 @@ def import_single_function_service(
293
291
 
294
292
  def import_class_service(
295
293
  function_def: api_pb2.Function,
296
- ser_cls,
294
+ service_function_hydration_data: api_pb2.Object,
295
+ class_id: str,
296
+ client: "modal.client.Client",
297
+ ser_user_cls: Optional[type],
297
298
  cls_args,
298
299
  cls_kwargs,
299
300
  ) -> Service:
@@ -304,11 +305,11 @@ def import_class_service(
304
305
  """
305
306
  active_app: Optional["modal.app._App"]
306
307
  service_deps: Optional[Sequence["modal._object._Object"]]
307
- cls: typing.Union[type, modal.cls.Cls]
308
+ cls_or_user_cls: typing.Union[type, modal.cls.Cls]
308
309
 
309
310
  if function_def.definition_type == api_pb2.Function.DEFINITION_TYPE_SERIALIZED:
310
- assert ser_cls is not None
311
- cls = ser_cls
311
+ assert ser_user_cls is not None
312
+ cls_or_user_cls = ser_user_cls
312
313
  else:
313
314
  # Load the module dynamically
314
315
  module = importlib.import_module(function_def.module_name)
@@ -327,22 +328,31 @@ def import_class_service(
327
328
 
328
329
  assert not function_def.use_method_name # new "placeholder methods" should not be invoked directly!
329
330
  cls_name = parts[0]
330
- cls = getattr(module, cls_name)
331
-
332
- if isinstance(cls, modal.cls.Cls):
333
- # The cls decorator is in global scope
334
- _cls = synchronizer._translate_in(cls)
335
- method_partials = _cls._get_partial_functions()
336
- service_function: _Function = _cls._class_service_function
337
- service_deps = service_function.deps(only_explicit_mounts=True)
338
- active_app = service_function.app
331
+ cls_or_user_cls = getattr(module, cls_name)
332
+
333
+ if isinstance(cls_or_user_cls, modal.cls.Cls):
334
+ _cls = synchronizer._translate_in(cls_or_user_cls)
335
+ class_service_function: _Function = _cls._get_class_service_function()
336
+ service_deps = class_service_function.deps(only_explicit_mounts=True)
337
+ active_app = class_service_function.app
339
338
  else:
340
- # Undecorated user class - find all methods
341
- method_partials = _find_partial_methods_for_user_cls(cls, _PartialFunctionFlags.all())
342
- service_deps = None
343
- active_app = None
339
+ # Undecorated user class (serialized or local scope-decoration).
340
+ service_deps = None # we can't infer service deps for now
341
+ active_app = get_active_app_fallback(function_def)
342
+ _client: "modal.client._Client" = synchronizer._translate_in(client)
343
+ _service_function: modal._functions._Function[Any, Any, Any] = modal._functions._Function._new_hydrated(
344
+ service_function_hydration_data.object_id,
345
+ _client,
346
+ service_function_hydration_data.function_handle_metadata,
347
+ is_another_app=True, # this skips re-loading the function, which is required since it doesn't have a loader
348
+ )
349
+ _cls = modal.cls._Cls.from_local(cls_or_user_cls, active_app, _service_function)
350
+ # hydration of the class itself - just sets the id and triggers some side effects
351
+ # that transfers metadata from the service function to the class. TODO: cleanup!
352
+ _cls._hydrate(class_id, _client, api_pb2.ClassHandleMetadata())
344
353
 
345
- user_cls_instance = get_user_class_instance(cls, cls_args, cls_kwargs)
354
+ method_partials: dict[str, "modal._partial_function._PartialFunction"] = _cls._get_partial_functions()
355
+ user_cls_instance = get_user_class_instance(_cls, cls_args, cls_kwargs)
346
356
 
347
357
  return ImportedClass(
348
358
  user_cls_instance,
@@ -351,3 +361,29 @@ def import_class_service(
351
361
  # TODO (elias/deven): instead of using method_partials here we should use a set of api_pb2.MethodDefinition
352
362
  method_partials,
353
363
  )
364
+
365
+
366
+ def get_active_app_fallback(function_def: api_pb2.Function) -> _App:
367
+ # This branch is reached in the special case that the imported function/class is:
368
+ # 1) not serialized, and
369
+ # 2) isn't a FunctionHandle - i.e, not decorated at definition time
370
+ # Look at all instantiated apps - if there is only one with the indicated name, use that one
371
+ app_name: Optional[str] = function_def.app_name or None # coalesce protobuf field to None
372
+ matching_apps = _App._all_apps.get(app_name, [])
373
+ if len(matching_apps) == 1:
374
+ active_app: _App = matching_apps[0]
375
+ return active_app
376
+
377
+ if len(matching_apps) > 1:
378
+ if app_name is not None:
379
+ warning_sub_message = f"app with the same name ('{app_name}')"
380
+ else:
381
+ warning_sub_message = "unnamed app"
382
+ logger.warning(
383
+ f"You have more than one {warning_sub_message}. "
384
+ "It's recommended to name all your Apps uniquely when using multiple apps"
385
+ )
386
+
387
+ # If we don't have an active app, create one on the fly
388
+ # The app object is used to carry the app layout etc
389
+ return _App()
modal/app.py CHANGED
@@ -598,7 +598,6 @@ class _App:
598
598
  scaledown_window: Optional[int] = None, # Max amount of time a container can remain idle before scaling down.
599
599
  proxy: Optional[_Proxy] = None, # Reference to a Modal Proxy to use in front of this function.
600
600
  retries: Optional[Union[int, Retries]] = None, # Number of times to retry each input in case of failure.
601
- allow_concurrent_inputs: Optional[int] = None, # Number of inputs the container may fetch to run concurrently.
602
601
  timeout: Optional[int] = None, # Maximum execution time of the function in seconds.
603
602
  name: Optional[str] = None, # Sets the Modal name of the function within the app
604
603
  is_generator: Optional[
@@ -625,6 +624,7 @@ class _App:
625
624
  keep_warm: Optional[int] = None, # Replaced with `min_containers`
626
625
  concurrency_limit: Optional[int] = None, # Replaced with `max_containers`
627
626
  container_idle_timeout: Optional[int] = None, # Replaced with `scaledown_window`
627
+ allow_concurrent_inputs: Optional[int] = None, # Replaced with the `@modal.concurrent` decorator
628
628
  _experimental_buffer_containers: Optional[int] = None, # Now stable API with `buffer_containers`
629
629
  ) -> _FunctionDecoratorType:
630
630
  """Decorator to register a new Modal [Function](/docs/reference/modal.Function) with this App."""
@@ -637,6 +637,14 @@ class _App:
637
637
  if image is None:
638
638
  image = self._get_default_image()
639
639
 
640
+ if allow_concurrent_inputs is not None:
641
+ deprecation_warning(
642
+ (2025, 4, 9),
643
+ "The `allow_concurrent_inputs` parameter is deprecated."
644
+ " Please use the `@modal.concurrent` decorator instead."
645
+ "\n\nSee https://modal.com/docs/guide/modal-1-0-migration for more information.",
646
+ )
647
+
640
648
  secrets = [*self._secrets, *secrets]
641
649
 
642
650
  def wrapped(
@@ -819,7 +827,6 @@ class _App:
819
827
  scaledown_window: Optional[int] = None, # Max amount of time a container can remain idle before scaling down.
820
828
  proxy: Optional[_Proxy] = None, # Reference to a Modal Proxy to use in front of this function.
821
829
  retries: Optional[Union[int, Retries]] = None, # Number of times to retry each input in case of failure.
822
- allow_concurrent_inputs: Optional[int] = None, # Number of inputs the container may fetch to run concurrently.
823
830
  timeout: Optional[int] = None, # Maximum execution time of the function in seconds.
824
831
  cloud: Optional[str] = None, # Cloud provider to run the function on. Possible values are aws, gcp, oci, auto.
825
832
  region: Optional[Union[str, Sequence[str]]] = None, # Region or regions to run the function on.
@@ -840,6 +847,7 @@ class _App:
840
847
  keep_warm: Optional[int] = None, # Replaced with `min_containers`
841
848
  concurrency_limit: Optional[int] = None, # Replaced with `max_containers`
842
849
  container_idle_timeout: Optional[int] = None, # Replaced with `scaledown_window`
850
+ allow_concurrent_inputs: Optional[int] = None, # Replaced with the `@modal.concurrent` decorator
843
851
  _experimental_buffer_containers: Optional[int] = None, # Now stable API with `buffer_containers`
844
852
  ) -> Callable[[Union[CLS_T, _PartialFunction]], CLS_T]:
845
853
  """
@@ -854,6 +862,14 @@ class _App:
854
862
  raise InvalidError("`region` and `_experimental_scheduler_placement` cannot be used together")
855
863
  scheduler_placement = SchedulerPlacement(region=region)
856
864
 
865
+ if allow_concurrent_inputs is not None:
866
+ deprecation_warning(
867
+ (2025, 4, 9),
868
+ "The `allow_concurrent_inputs` parameter is deprecated."
869
+ " Please use the `@modal.concurrent` decorator instead."
870
+ "\n\nSee https://modal.com/docs/guide/modal-1-0-migration for more information.",
871
+ )
872
+
857
873
  def wrapper(wrapped_cls: Union[CLS_T, _PartialFunction]) -> CLS_T:
858
874
  # Check if the decorated object is a class
859
875
  if isinstance(wrapped_cls, _PartialFunction):
modal/app.pyi CHANGED
@@ -182,7 +182,6 @@ class _App:
182
182
  scaledown_window: typing.Optional[int] = None,
183
183
  proxy: typing.Optional[modal.proxy._Proxy] = None,
184
184
  retries: typing.Union[int, modal.retries.Retries, None] = None,
185
- allow_concurrent_inputs: typing.Optional[int] = None,
186
185
  timeout: typing.Optional[int] = None,
187
186
  name: typing.Optional[str] = None,
188
187
  is_generator: typing.Optional[bool] = None,
@@ -200,6 +199,7 @@ class _App:
200
199
  keep_warm: typing.Optional[int] = None,
201
200
  concurrency_limit: typing.Optional[int] = None,
202
201
  container_idle_timeout: typing.Optional[int] = None,
202
+ allow_concurrent_inputs: typing.Optional[int] = None,
203
203
  _experimental_buffer_containers: typing.Optional[int] = None,
204
204
  ) -> _FunctionDecoratorType: ...
205
205
  @typing_extensions.dataclass_transform(
@@ -232,7 +232,6 @@ class _App:
232
232
  scaledown_window: typing.Optional[int] = None,
233
233
  proxy: typing.Optional[modal.proxy._Proxy] = None,
234
234
  retries: typing.Union[int, modal.retries.Retries, None] = None,
235
- allow_concurrent_inputs: typing.Optional[int] = None,
236
235
  timeout: typing.Optional[int] = None,
237
236
  cloud: typing.Optional[str] = None,
238
237
  region: typing.Union[str, collections.abc.Sequence[str], None] = None,
@@ -247,6 +246,7 @@ class _App:
247
246
  keep_warm: typing.Optional[int] = None,
248
247
  concurrency_limit: typing.Optional[int] = None,
249
248
  container_idle_timeout: typing.Optional[int] = None,
249
+ allow_concurrent_inputs: typing.Optional[int] = None,
250
250
  _experimental_buffer_containers: typing.Optional[int] = None,
251
251
  ) -> collections.abc.Callable[[typing.Union[CLS_T, modal._partial_function._PartialFunction]], CLS_T]: ...
252
252
  async def spawn_sandbox(
@@ -422,7 +422,6 @@ class App:
422
422
  scaledown_window: typing.Optional[int] = None,
423
423
  proxy: typing.Optional[modal.proxy.Proxy] = None,
424
424
  retries: typing.Union[int, modal.retries.Retries, None] = None,
425
- allow_concurrent_inputs: typing.Optional[int] = None,
426
425
  timeout: typing.Optional[int] = None,
427
426
  name: typing.Optional[str] = None,
428
427
  is_generator: typing.Optional[bool] = None,
@@ -440,6 +439,7 @@ class App:
440
439
  keep_warm: typing.Optional[int] = None,
441
440
  concurrency_limit: typing.Optional[int] = None,
442
441
  container_idle_timeout: typing.Optional[int] = None,
442
+ allow_concurrent_inputs: typing.Optional[int] = None,
443
443
  _experimental_buffer_containers: typing.Optional[int] = None,
444
444
  ) -> _FunctionDecoratorType: ...
445
445
  @typing_extensions.dataclass_transform(
@@ -472,7 +472,6 @@ class App:
472
472
  scaledown_window: typing.Optional[int] = None,
473
473
  proxy: typing.Optional[modal.proxy.Proxy] = None,
474
474
  retries: typing.Union[int, modal.retries.Retries, None] = None,
475
- allow_concurrent_inputs: typing.Optional[int] = None,
476
475
  timeout: typing.Optional[int] = None,
477
476
  cloud: typing.Optional[str] = None,
478
477
  region: typing.Union[str, collections.abc.Sequence[str], None] = None,
@@ -487,6 +486,7 @@ class App:
487
486
  keep_warm: typing.Optional[int] = None,
488
487
  concurrency_limit: typing.Optional[int] = None,
489
488
  container_idle_timeout: typing.Optional[int] = None,
489
+ allow_concurrent_inputs: typing.Optional[int] = None,
490
490
  _experimental_buffer_containers: typing.Optional[int] = None,
491
491
  ) -> collections.abc.Callable[[typing.Union[CLS_T, modal.partial_function.PartialFunction]], CLS_T]: ...
492
492
 
modal/client.pyi CHANGED
@@ -31,7 +31,7 @@ class _Client:
31
31
  server_url: str,
32
32
  client_type: int,
33
33
  credentials: typing.Optional[tuple[str, str]],
34
- version: str = "0.73.158",
34
+ version: str = "0.73.160",
35
35
  ): ...
36
36
  def is_closed(self) -> bool: ...
37
37
  @property
@@ -93,7 +93,7 @@ class Client:
93
93
  server_url: str,
94
94
  client_type: int,
95
95
  credentials: typing.Optional[tuple[str, str]],
96
- version: str = "0.73.158",
96
+ version: str = "0.73.160",
97
97
  ): ...
98
98
  def is_closed(self) -> bool: ...
99
99
  @property
modal/cls.py CHANGED
@@ -139,7 +139,7 @@ def _bind_instance_method(cls: "_Cls", service_function: _Function, method_name:
139
139
  # ugly - needed for .local() TODO (elias): Clean up!
140
140
  partial_function.raw_f,
141
141
  user_cls=cls._user_cls,
142
- serialized=service_function.info.is_serialized(),
142
+ serialized=True, # service_function.info.is_serialized(),
143
143
  )
144
144
 
145
145
  fun._obj = service_function._obj
modal/functions.pyi CHANGED
@@ -200,11 +200,11 @@ class Function(
200
200
 
201
201
  _call_generator_nowait: ___call_generator_nowait_spec[typing_extensions.Self]
202
202
 
203
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
203
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
204
204
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
205
205
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
206
206
 
207
- remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
207
+ remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
208
208
 
209
209
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
210
210
  def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
@@ -219,19 +219,19 @@ class Function(
219
219
  self, *args: modal._functions.P.args, **kwargs: modal._functions.P.kwargs
220
220
  ) -> modal._functions.OriginalReturnType: ...
221
221
 
222
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
222
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
223
223
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
224
224
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
225
225
 
226
226
  _experimental_spawn: ___experimental_spawn_spec[
227
- modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
227
+ modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
228
228
  ]
229
229
 
230
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
230
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
231
231
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
232
232
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
233
233
 
234
- spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
234
+ spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
235
235
 
236
236
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]: ...
237
237
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: modal
3
- Version: 0.73.158
3
+ Version: 0.73.160
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -2,7 +2,7 @@ modal/__init__.py,sha256=7wz1AT_bpWJJEzXsAo3QMb7i87y7UGXwfneb0bGDhRg,2502
2
2
  modal/__main__.py,sha256=CgIjP8m1xJjjd4AXc-delmR6LdBCZclw2A_V38CFIio,2870
3
3
  modal/_clustered_functions.py,sha256=kTf-9YBXY88NutC1akI-gCbvf01RhMPCw-zoOI_YIUE,2700
4
4
  modal/_clustered_functions.pyi,sha256=vllkegc99A0jrUOWa8mdlSbdp6uz36TsHhGxysAOpaQ,771
5
- modal/_container_entrypoint.py,sha256=XyqJPvzX0YMqviIIz-9bsD6HMrPsboU4A1yfgTloTSA,29302
5
+ modal/_container_entrypoint.py,sha256=Dhv6YV2ejwluaWi3pHTxIja8U7IXtgD1vsCeH9wHCKM,29002
6
6
  modal/_functions.py,sha256=M94gzMA9xfW9086djoG2yYFVihcslKnsleacmNbVrG0,74996
7
7
  modal/_ipython.py,sha256=TW1fkVOmZL3YYqdS2YlM1hqpf654Yf8ZyybHdBnlhSw,301
8
8
  modal/_location.py,sha256=joiX-0ZeutEUDTrrqLF1GHXCdVLF-rHzstocbMcd_-k,366
@@ -19,14 +19,14 @@ modal/_tunnel.py,sha256=zTBxBiuH1O22tS1OliAJdIsSmaZS8PlnifS_6S5z-mk,6320
19
19
  modal/_tunnel.pyi,sha256=JmmDYAy9F1FpgJ_hWx0xkom2nTOFQjn4mTPYlU3PFo4,1245
20
20
  modal/_type_manager.py,sha256=DWjgmjYJuOagw2erin506UUbG2H5UzZCFEekS-7hmfA,9087
21
21
  modal/_watcher.py,sha256=K6LYnlmSGQB4tWWI9JADv-tvSvQ1j522FwT71B51CX8,3584
22
- modal/app.py,sha256=w00bV9cjABAsS2ExE7zb1jY6Q_snXYmdKa3xRFg8iXA,47428
23
- modal/app.pyi,sha256=pUEqciyGZ446sc_QoG8XcQ_oc6oU-U4dqjkxjhgOX98,26968
22
+ modal/app.py,sha256=bJp7W3liuVG2VwWkG31tMFogDh84EKppzP8YJFWl3eQ,48140
23
+ modal/app.pyi,sha256=SkqXNrdnGIZ4MmNNvpGtzNLoUdyuvi9IjQQR_DRiRHk,26968
24
24
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
25
25
  modal/client.py,sha256=U-YKSw0n7J1ZLREt9cbEJCtmHe5YoPKFxl0xlkan2yc,15565
26
- modal/client.pyi,sha256=2xDnuSzcyAojtKfIiW4h13jQXTDGW_VqMrToQ_VwKVM,7661
26
+ modal/client.pyi,sha256=LAaMk1gBW_D0PsrEYs-n0asL1e1sqvKdqT2ew4QV9d4,7661
27
27
  modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
28
28
  modal/cloud_bucket_mount.pyi,sha256=30T3K1a89l6wzmEJ_J9iWv9SknoGqaZDx59Xs-ZQcmk,1607
29
- modal/cls.py,sha256=jlQE7qEwGG49FCSqqzYkcedi619G3K4PNnJCtPO8y_w,32954
29
+ modal/cls.py,sha256=8tvSw7QFTS1FnX2MXaxagu3KwuR6y_DMwhqHv3MZ0Nk,32963
30
30
  modal/cls.pyi,sha256=pTYO9JsRENmsa5pDgzfoRJGm_NpCvEjEx--vs-jJkj8,10902
31
31
  modal/config.py,sha256=FlqVyh6LVukMahhmEGQVTwWtwtfoPfHqEo3GDn13EOA,11687
32
32
  modal/container_process.py,sha256=vvyK3DVPUMsuqvkKdUiQ49cDLF9JawGrxpglLk5vfgI,6208
@@ -40,7 +40,7 @@ modal/file_io.py,sha256=lcMs_E9Xfm0YX1t9U2wNIBPnqHRxmImqjLW1GHqVmyg,20945
40
40
  modal/file_io.pyi,sha256=NTRft1tbPSWf9TlWVeZmTlgB5AZ_Zhu2srWIrWr7brk,9445
41
41
  modal/file_pattern_matcher.py,sha256=trosX-Bp7dOubudN1bLLhRAoidWy1TcoaR4Pv8CedWw,6497
42
42
  modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
43
- modal/functions.pyi,sha256=Bg_zlYSHMYuEXQYLcFU-j1Kwq5zHQ9jggMA_5ZO_du8,14785
43
+ modal/functions.pyi,sha256=m1PL2pwO-lnGV0uZDVCmzZ_v7Mu8ISRtxmxS15aEIAQ,14785
44
44
  modal/gpu.py,sha256=Kbhs_u49FaC2Zi0TjCdrpstpRtT5eZgecynmQi5IZVE,6752
45
45
  modal/image.py,sha256=HtkKomhX4inozqSRi7lf5Vt9IEqCnVHn5bEo59hD64A,92835
46
46
  modal/image.pyi,sha256=iWclz2rxaP-LSsYMgU0X3ZcN5mEFvpyKzIPKJbohmsg,25591
@@ -82,14 +82,14 @@ modal/token_flow.pyi,sha256=0XV3d-9CGQL3qjPdw3RgwIFVqqxo8Z-u044_mkgAM3o,2064
82
82
  modal/volume.py,sha256=JAWeDvoAG95tMBv-fYIERyHsJPS_X_xGpxRRmYtb6j0,30096
83
83
  modal/volume.pyi,sha256=kTsXarphjZILXci84LQy7EyC84eXUs5-7D62IM5q3eE,12491
84
84
  modal/_runtime/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
85
- modal/_runtime/asgi.py,sha256=vmbrREjdCnxAeUyoUd1vtv7PfcR86IR0R2Jp1J6rEok,22435
85
+ modal/_runtime/asgi.py,sha256=KNarxvZI9z8fnmZl2vbkWTjnoLXs9kqOahkrbsTLkyc,22429
86
86
  modal/_runtime/container_io_manager.py,sha256=-EpE47kL759b87SsQaaGMuqRBbvS8gRHoyAOIKzGRTY,44045
87
87
  modal/_runtime/container_io_manager.pyi,sha256=wRd2wHMFru0NmNgiCBVdDTrJGkeVZsZvWwA1fzn8wi8,17009
88
88
  modal/_runtime/execution_context.py,sha256=E6ofm6j1POXGPxS841X3V7JU6NheVb8OkQc7JpLq4Kg,2712
89
89
  modal/_runtime/execution_context.pyi,sha256=wQZwMNADExkeNdB9yKX0PPojovxlFHbap3441wAsiMY,634
90
90
  modal/_runtime/gpu_memory_snapshot.py,sha256=tA3m1d1cwnmHpvpCeN_WijDd6n8byn7LWlpicbIxiOI,3144
91
91
  modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5163
92
- modal/_runtime/user_code_imports.py,sha256=Q3EXhRObBvBWwUrrXI3wfVPSlGqNr5UtchFjMWeoVS0,14828
92
+ modal/_runtime/user_code_imports.py,sha256=kAv37Pl1TmGKduv0Kozum0xNTD42bDLloSIsT7zf84o,16884
93
93
  modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
94
94
  modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
95
95
  modal/_utils/async_utils.py,sha256=b2TJyKY1Hq7df7M-fo3qlFM95mGdo3dCuqRPPcV5hsE,27445
@@ -170,10 +170,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
170
170
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
171
171
  modal_version/__init__.py,sha256=wiJQ53c-OMs0Xf1UeXOxQ7FwlV1VzIjnX6o-pRYZ_Pk,470
172
172
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
173
- modal_version/_version_generated.py,sha256=W-Cjy5h17CL7l8Q5DFjspeFnQc1xP0KQ83zpkDXqVD4,150
174
- modal-0.73.158.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
175
- modal-0.73.158.dist-info/METADATA,sha256=V8G3VKDYed7Y6hnUEmwQNtFOrBO-0ORRHED2hfH9yxk,2453
176
- modal-0.73.158.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
177
- modal-0.73.158.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
178
- modal-0.73.158.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
179
- modal-0.73.158.dist-info/RECORD,,
173
+ modal_version/_version_generated.py,sha256=r_EYxPHmCmDo6PJZdlVut-xOD_e83qaBcdIllk231Is,150
174
+ modal-0.73.160.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
175
+ modal-0.73.160.dist-info/METADATA,sha256=Rt5Xjuo-Iv-03D7FM0yjkhqbjnwINr9ZxIVTRw2oNVg,2453
176
+ modal-0.73.160.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
177
+ modal-0.73.160.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
178
+ modal-0.73.160.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
179
+ modal-0.73.160.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
 
3
3
  # Note: Reset this value to -1 whenever you make a minor `0.X` release of the client.
4
- build_number = 158 # git: 860490c
4
+ build_number = 160 # git: 2ef9846