modal 0.73.76__py3-none-any.whl → 0.73.77__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.
modal/_functions.py CHANGED
@@ -927,7 +927,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
927
927
  def _bind_parameters(
928
928
  self,
929
929
  obj: "modal.cls._Obj",
930
- options: Optional[api_pb2.FunctionOptions],
930
+ options: Optional["modal.cls._ServiceOptions"],
931
931
  args: Sized,
932
932
  kwargs: dict[str, Any],
933
933
  ) -> "_Function":
@@ -978,10 +978,35 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
978
978
 
979
979
  environment_name = _get_environment_name(None, resolver)
980
980
  assert parent is not None and parent.is_hydrated
981
+
982
+ if options:
983
+ volume_mounts = [
984
+ api_pb2.VolumeMount(
985
+ mount_path=path,
986
+ volume_id=volume.object_id,
987
+ allow_background_commits=True,
988
+ )
989
+ for path, volume in options.validated_volumes
990
+ ]
991
+ options_pb = api_pb2.FunctionOptions(
992
+ secret_ids=[s.object_id for s in options.secrets],
993
+ replace_secret_ids=bool(options.secrets),
994
+ resources=options.resources,
995
+ retry_policy=options.retry_policy,
996
+ concurrency_limit=options.concurrency_limit,
997
+ timeout_secs=options.timeout_secs,
998
+ task_idle_timeout_secs=options.task_idle_timeout_secs,
999
+ replace_volume_mounts=len(volume_mounts) > 0,
1000
+ volume_mounts=volume_mounts,
1001
+ target_concurrent_inputs=options.target_concurrent_inputs,
1002
+ )
1003
+ else:
1004
+ options_pb = None
1005
+
981
1006
  req = api_pb2.FunctionBindParamsRequest(
982
1007
  function_id=parent.object_id,
983
1008
  serialized_params=serialized_params,
984
- function_options=options,
1009
+ function_options=options_pb,
985
1010
  environment_name=environment_name
986
1011
  or "", # TODO: investigate shouldn't environment name always be specified here?
987
1012
  )
@@ -989,7 +1014,12 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
989
1014
  response = await retry_transient_errors(parent._client.stub.FunctionBindParams, req)
990
1015
  param_bound_func._hydrate(response.bound_function_id, parent._client, response.handle_metadata)
991
1016
 
992
- fun: _Function = _Function._from_loader(_load, "Function(parametrized)", hydrate_lazily=True)
1017
+ def _deps():
1018
+ if options:
1019
+ return [v for _, v in options.validated_volumes] + list(options.secrets)
1020
+ return []
1021
+
1022
+ fun: _Function = _Function._from_loader(_load, "Function(parametrized)", hydrate_lazily=True, deps=_deps)
993
1023
 
994
1024
  fun._info = self._info
995
1025
  fun._obj = obj
modal/client.pyi CHANGED
@@ -27,7 +27,7 @@ class _Client:
27
27
  _snapshotted: bool
28
28
 
29
29
  def __init__(
30
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.76"
30
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.77"
31
31
  ): ...
32
32
  def is_closed(self) -> bool: ...
33
33
  @property
@@ -85,7 +85,7 @@ class Client:
85
85
  _snapshotted: bool
86
86
 
87
87
  def __init__(
88
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.76"
88
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.77"
89
89
  ): ...
90
90
  def is_closed(self) -> bool: ...
91
91
  @property
modal/cls.py CHANGED
@@ -1,4 +1,5 @@
1
1
  # Copyright Modal Labs 2022
2
+ import dataclasses
2
3
  import inspect
3
4
  import os
4
5
  import typing
@@ -72,6 +73,18 @@ def _get_class_constructor_signature(user_cls: type) -> inspect.Signature:
72
73
  return inspect.Signature(constructor_parameters)
73
74
 
74
75
 
76
+ @dataclasses.dataclass()
77
+ class _ServiceOptions:
78
+ secrets: typing.Collection[_Secret]
79
+ resources: Optional[api_pb2.Resources]
80
+ retry_policy: Optional[api_pb2.FunctionRetryPolicy]
81
+ concurrency_limit: Optional[int]
82
+ timeout_secs: Optional[int]
83
+ task_idle_timeout_secs: Optional[int]
84
+ validated_volumes: typing.Sequence[tuple[str, _Volume]]
85
+ target_concurrent_inputs: Optional[int]
86
+
87
+
75
88
  def _bind_instance_method(cls: "_Cls", service_function: _Function, method_name: str):
76
89
  """Binds an "instance service function" to a specific method using metadata for that method
77
90
 
@@ -144,12 +157,13 @@ class _Obj:
144
157
  _kwargs: dict[str, Any]
145
158
 
146
159
  _instance_service_function: Optional[_Function] = None # this gets set lazily
160
+ _options: Optional[_ServiceOptions]
147
161
 
148
162
  def __init__(
149
163
  self,
150
164
  cls: "_Cls",
151
165
  user_cls: Optional[type], # this would be None in case of lookups
152
- options: Optional[api_pb2.FunctionOptions],
166
+ options: Optional[_ServiceOptions],
153
167
  args,
154
168
  kwargs,
155
169
  ):
@@ -354,7 +368,7 @@ class _Cls(_Object, type_prefix="cs"):
354
368
  """
355
369
 
356
370
  _class_service_function: Optional[_Function] # The _Function (read "service") serving *all* methods of the class
357
- _options: Optional[api_pb2.FunctionOptions]
371
+ _options: Optional[_ServiceOptions] # TODO: typed dict/dataclass?
358
372
 
359
373
  _app: Optional["modal.app._App"] = None # not set for lookups
360
374
  _name: Optional[str]
@@ -595,28 +609,16 @@ class _Cls(_Object, type_prefix="cs"):
595
609
  else:
596
610
  resources = None
597
611
 
598
- volume_mounts = [
599
- api_pb2.VolumeMount(
600
- mount_path=path,
601
- volume_id=volume.object_id,
602
- allow_background_commits=True,
603
- )
604
- for path, volume in validate_volumes(volumes)
605
- ]
606
- replace_volume_mounts = len(volume_mounts) > 0
607
-
608
612
  cls = self.clone()
609
- cls._options = api_pb2.FunctionOptions(
610
- replace_secret_ids=bool(secrets),
611
- secret_ids=[secret.object_id for secret in secrets],
613
+ cls._options = _ServiceOptions(
614
+ secrets=secrets,
612
615
  resources=resources,
613
616
  retry_policy=retry_policy,
614
617
  # TODO(michael) Update the protos to use the new terminology
615
618
  concurrency_limit=max_containers,
616
619
  task_idle_timeout_secs=scaledown_window,
617
620
  timeout_secs=timeout,
618
- replace_volume_mounts=replace_volume_mounts,
619
- volume_mounts=volume_mounts,
621
+ validated_volumes=validate_volumes(volumes),
620
622
  target_concurrent_inputs=allow_concurrent_inputs,
621
623
  )
622
624
 
modal/cls.pyi CHANGED
@@ -22,6 +22,31 @@ T = typing.TypeVar("T")
22
22
 
23
23
  def _use_annotation_parameters(user_cls: type) -> bool: ...
24
24
  def _get_class_constructor_signature(user_cls: type) -> inspect.Signature: ...
25
+
26
+ class _ServiceOptions:
27
+ secrets: typing.Collection[modal.secret._Secret]
28
+ resources: typing.Optional[modal_proto.api_pb2.Resources]
29
+ retry_policy: typing.Optional[modal_proto.api_pb2.FunctionRetryPolicy]
30
+ concurrency_limit: typing.Optional[int]
31
+ timeout_secs: typing.Optional[int]
32
+ task_idle_timeout_secs: typing.Optional[int]
33
+ validated_volumes: typing.Sequence[tuple[str, modal.volume._Volume]]
34
+ target_concurrent_inputs: typing.Optional[int]
35
+
36
+ def __init__(
37
+ self,
38
+ secrets: typing.Collection[modal.secret._Secret],
39
+ resources: typing.Optional[modal_proto.api_pb2.Resources],
40
+ retry_policy: typing.Optional[modal_proto.api_pb2.FunctionRetryPolicy],
41
+ concurrency_limit: typing.Optional[int],
42
+ timeout_secs: typing.Optional[int],
43
+ task_idle_timeout_secs: typing.Optional[int],
44
+ validated_volumes: typing.Sequence[tuple[str, modal.volume._Volume]],
45
+ target_concurrent_inputs: typing.Optional[int],
46
+ ) -> None: ...
47
+ def __repr__(self): ...
48
+ def __eq__(self, other): ...
49
+
25
50
  def _bind_instance_method(cls: _Cls, service_function: modal._functions._Function, method_name: str): ...
26
51
 
27
52
  class _Obj:
@@ -32,14 +57,10 @@ class _Obj:
32
57
  _args: tuple[typing.Any, ...]
33
58
  _kwargs: dict[str, typing.Any]
34
59
  _instance_service_function: typing.Optional[modal._functions._Function]
60
+ _options: typing.Optional[_ServiceOptions]
35
61
 
36
62
  def __init__(
37
- self,
38
- cls: _Cls,
39
- user_cls: typing.Optional[type],
40
- options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
41
- args,
42
- kwargs,
63
+ self, cls: _Cls, user_cls: typing.Optional[type], options: typing.Optional[_ServiceOptions], args, kwargs
43
64
  ): ...
44
65
  def _cached_service_function(self) -> modal._functions._Function: ...
45
66
  def _get_parameter_values(self) -> dict[str, typing.Any]: ...
@@ -64,14 +85,10 @@ class Obj:
64
85
  _args: tuple[typing.Any, ...]
65
86
  _kwargs: dict[str, typing.Any]
66
87
  _instance_service_function: typing.Optional[modal.functions.Function]
88
+ _options: typing.Optional[_ServiceOptions]
67
89
 
68
90
  def __init__(
69
- self,
70
- cls: Cls,
71
- user_cls: typing.Optional[type],
72
- options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
73
- args,
74
- kwargs,
91
+ self, cls: Cls, user_cls: typing.Optional[type], options: typing.Optional[_ServiceOptions], args, kwargs
75
92
  ): ...
76
93
  def _cached_service_function(self) -> modal.functions.Function: ...
77
94
  def _get_parameter_values(self) -> dict[str, typing.Any]: ...
@@ -94,7 +111,7 @@ class Obj:
94
111
 
95
112
  class _Cls(modal._object._Object):
96
113
  _class_service_function: typing.Optional[modal._functions._Function]
97
- _options: typing.Optional[modal_proto.api_pb2.FunctionOptions]
114
+ _options: typing.Optional[_ServiceOptions]
98
115
  _app: typing.Optional[modal.app._App]
99
116
  _name: typing.Optional[str]
100
117
  _method_metadata: typing.Optional[dict[str, modal_proto.api_pb2.FunctionHandleMetadata]]
@@ -154,7 +171,7 @@ class _Cls(modal._object._Object):
154
171
 
155
172
  class Cls(modal.object.Object):
156
173
  _class_service_function: typing.Optional[modal.functions.Function]
157
- _options: typing.Optional[modal_proto.api_pb2.FunctionOptions]
174
+ _options: typing.Optional[_ServiceOptions]
158
175
  _app: typing.Optional[modal.app.App]
159
176
  _name: typing.Optional[str]
160
177
  _method_metadata: typing.Optional[dict[str, modal_proto.api_pb2.FunctionHandleMetadata]]
modal/functions.pyi CHANGED
@@ -103,7 +103,7 @@ class Function(
103
103
  def _bind_parameters(
104
104
  self,
105
105
  obj: modal.cls.Obj,
106
- options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
106
+ options: typing.Optional[modal.cls._ServiceOptions],
107
107
  args: collections.abc.Sized,
108
108
  kwargs: dict[str, typing.Any],
109
109
  ) -> Function: ...
@@ -198,11 +198,11 @@ class Function(
198
198
 
199
199
  _call_generator_nowait: ___call_generator_nowait_spec[typing_extensions.Self]
200
200
 
201
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
201
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
202
202
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
203
203
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
204
204
 
205
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
205
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
206
206
 
207
207
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
208
208
  def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
@@ -217,19 +217,19 @@ class Function(
217
217
  self, *args: modal._functions.P.args, **kwargs: modal._functions.P.kwargs
218
218
  ) -> modal._functions.OriginalReturnType: ...
219
219
 
220
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
220
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
221
221
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
222
222
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
223
223
 
224
224
  _experimental_spawn: ___experimental_spawn_spec[
225
- modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
225
+ modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
226
226
  ]
227
227
 
228
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
228
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
229
229
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
230
230
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
231
231
 
232
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
232
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
233
233
 
234
234
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]: ...
235
235
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: modal
3
- Version: 0.73.76
3
+ Version: 0.73.77
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -3,7 +3,7 @@ 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
5
  modal/_container_entrypoint.py,sha256=arhkIoF8nQNfa4iwYGSoqN3QMDg5M38QNAODXC8TlKc,29301
6
- modal/_functions.py,sha256=LkzRfcex420bHWN2s5Aqbtm9cg7PaAcuG-H2jNz1JfE,71459
6
+ modal/_functions.py,sha256=OR76wIl-Hq5Q-mId_FzcEhpUdU6Lq40FDvnMLvAzgUM,72740
7
7
  modal/_ipython.py,sha256=TW1fkVOmZL3YYqdS2YlM1hqpf654Yf8ZyybHdBnlhSw,301
8
8
  modal/_location.py,sha256=joiX-0ZeutEUDTrrqLF1GHXCdVLF-rHzstocbMcd_-k,366
9
9
  modal/_object.py,sha256=ItQcsMNkz9Y3kdTsvfNarbW-paJ2qabDyQ7njaqY0XI,11359
@@ -22,11 +22,11 @@ modal/app.py,sha256=kF3frIt4eRKVYYCjusMMhKJpO_lDdm2z37HOXPwpjT8,45506
22
22
  modal/app.pyi,sha256=tZFbcsu20SuvfB2puxCyuXLFNJ9bQulzag55rVpgZmc,26827
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=8SQawr7P1PNUCq1UmJMUQXG2jIo4Nmdcs311XqrNLRE,15276
25
- modal/client.pyi,sha256=BY_85JYs5yMmzZbJ4XMfajA48crezjBUTGtZ8RUi-gc,7593
25
+ modal/client.pyi,sha256=Pz702Nbzk12XDU_wdlGt1PTWzNgS5-BL6Rj-Lf5Z1bE,7593
26
26
  modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
27
27
  modal/cloud_bucket_mount.pyi,sha256=30T3K1a89l6wzmEJ_J9iWv9SknoGqaZDx59Xs-ZQcmk,1607
28
- modal/cls.py,sha256=pQqLU_vlgHD2aglDQAoVuzt0BS3pUkRfC7oHG_fsbV4,30700
29
- modal/cls.pyi,sha256=MAx5J72UhD5l2qbH_sTpZDP34PlI4P71OBLSWOqvE68,9300
28
+ modal/cls.py,sha256=0LKIHboBFRY291VsG0ick_6KZk2ArK1RLus9mlTZtJ8,30726
29
+ modal/cls.pyi,sha256=ZJUwtRaQBGlM6tphvnv49FHBVDSgttMdD_LnYyRSKJM,10302
30
30
  modal/config.py,sha256=Boz1bPzaG-k5Grjq6y6fAELH1N_gTuYDnpB6FODzCPo,11710
31
31
  modal/container_process.py,sha256=WTqLn01dJPVkPpwR_0w_JH96ceN5mV4TGtiu1ZR2RRA,6108
32
32
  modal/container_process.pyi,sha256=Hf0J5JyDdCCXBJSKx6gvkPOo0XrztCm78xzxamtzUjQ,2828
@@ -41,7 +41,7 @@ modal/file_io.py,sha256=lcMs_E9Xfm0YX1t9U2wNIBPnqHRxmImqjLW1GHqVmyg,20945
41
41
  modal/file_io.pyi,sha256=NTRft1tbPSWf9TlWVeZmTlgB5AZ_Zhu2srWIrWr7brk,9445
42
42
  modal/file_pattern_matcher.py,sha256=trosX-Bp7dOubudN1bLLhRAoidWy1TcoaR4Pv8CedWw,6497
43
43
  modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
44
- modal/functions.pyi,sha256=7m3qSR7dKUPcSZycZ4Y1VaySjdBZI0_MmR8pRrbHzxE,14387
44
+ modal/functions.pyi,sha256=D-PDJfSbwqMDXdq7Bxu2ErZRENo-tRgu_zPoB-jl0OU,14377
45
45
  modal/gpu.py,sha256=Kbhs_u49FaC2Zi0TjCdrpstpRtT5eZgecynmQi5IZVE,6752
46
46
  modal/image.py,sha256=adMUpS7WrCu-M78BWslz2r6GPviy4qPvd5Dh-dBIrrk,90257
47
47
  modal/image.pyi,sha256=L7aZUOElSGtNHmFHz1RgKP1cG5paiXt_EzylrwBwzVk,25004
@@ -168,10 +168,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
168
168
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
169
169
  modal_version/__init__.py,sha256=wiJQ53c-OMs0Xf1UeXOxQ7FwlV1VzIjnX6o-pRYZ_Pk,470
170
170
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
171
- modal_version/_version_generated.py,sha256=jmVn-W1r2Lr0q91iDTY2ZmvlYDwd2coi9KKtw_i03Jk,149
172
- modal-0.73.76.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
173
- modal-0.73.76.dist-info/METADATA,sha256=XZV1Q5CvoLdg2NmRCtLXu5CfAd8SISK_6cRFO_bySec,2452
174
- modal-0.73.76.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
175
- modal-0.73.76.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
176
- modal-0.73.76.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
177
- modal-0.73.76.dist-info/RECORD,,
171
+ modal_version/_version_generated.py,sha256=3PMwQ2aqnXHX71h9sw3Oe5RJdzScfCrLIwFoBDCVDqc,149
172
+ modal-0.73.77.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
173
+ modal-0.73.77.dist-info/METADATA,sha256=Qw57v7B3T8x4RkVU1V0YxSGksoaZbwXDKtiE_IYMOwU,2452
174
+ modal-0.73.77.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
175
+ modal-0.73.77.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
176
+ modal-0.73.77.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
177
+ modal-0.73.77.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 = 76 # git: d55968d
4
+ build_number = 77 # git: a8e6edd