modal 1.0.4.dev10__py3-none-any.whl → 1.0.5.dev1__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.
@@ -396,7 +396,7 @@ class _WarnIfGeneratorIsNotConsumed:
396
396
  return await self.gen.aclose()
397
397
 
398
398
 
399
- synchronize_api(_WarnIfGeneratorIsNotConsumed)
399
+ _BlockingWarnIfGeneratorIsNotConsumed = synchronize_api(_WarnIfGeneratorIsNotConsumed)
400
400
 
401
401
 
402
402
  class _WarnIfNonWrappedGeneratorIsNotConsumed(_WarnIfGeneratorIsNotConsumed):
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 = "1.0.4.dev10",
34
+ version: str = "1.0.5.dev1",
35
35
  ): ...
36
36
  def is_closed(self) -> bool: ...
37
37
  @property
@@ -94,7 +94,7 @@ class Client:
94
94
  server_url: str,
95
95
  client_type: int,
96
96
  credentials: typing.Optional[tuple[str, str]],
97
- version: str = "1.0.4.dev10",
97
+ version: str = "1.0.5.dev1",
98
98
  ): ...
99
99
  def is_closed(self) -> bool: ...
100
100
  @property
modal/cls.py CHANGED
@@ -75,6 +75,7 @@ def _get_class_constructor_signature(user_cls: type) -> inspect.Signature:
75
75
 
76
76
  @dataclasses.dataclass()
77
77
  class _ServiceOptions:
78
+ # Note that default values should always be "untruthy" so we can detect when they are not set
78
79
  secrets: typing.Collection[_Secret] = ()
79
80
  validated_volumes: typing.Sequence[tuple[str, _Volume]] = ()
80
81
  resources: Optional[api_pb2.Resources] = None
@@ -88,6 +89,25 @@ class _ServiceOptions:
88
89
  batch_max_size: Optional[int] = None
89
90
  batch_wait_ms: Optional[int] = None
90
91
 
92
+ def merge_options(self, new_options: "_ServiceOptions") -> "_ServiceOptions":
93
+ """Implement protobuf-like MergeFrom semantics for this dataclass.
94
+
95
+ This mostly exists to support "stacking" of `.with_options()` calls.
96
+ """
97
+ new_options_dict = dataclasses.asdict(new_options)
98
+
99
+ # Resources needs special merge handling because individual fields are parameters in the public API
100
+ merged_resources = api_pb2.Resources()
101
+ if self.resources:
102
+ merged_resources.MergeFrom(self.resources)
103
+ if new_resources := new_options_dict.pop("resources"):
104
+ merged_resources.MergeFrom(new_resources)
105
+ self.resources = merged_resources
106
+
107
+ for key, value in new_options_dict.items():
108
+ if value: # Only overwrite data when the value was set in the new options
109
+ setattr(self, key, value)
110
+
91
111
 
92
112
  def _bind_instance_method(cls: "_Cls", service_function: _Function, method_name: str):
93
113
  """Binds an "instance service function" to a specific method using metadata for that method
@@ -664,15 +684,32 @@ More information on class parameterization can be found here: https://modal.com/
664
684
  container_idle_timeout: Optional[int] = None, # Now called `scaledown_window`
665
685
  allow_concurrent_inputs: Optional[int] = None, # See `.with_concurrency`
666
686
  ) -> "_Cls":
667
- """Create an instance of the Cls with configuration options overridden with new values.
687
+ """Override the static Function configuration at runtime.
688
+
689
+ This method will return a new instance of the cls that will autoscale independently of the
690
+ original instance. Note that options cannot be "unset" with this method (i.e., if a GPU
691
+ is configured in the `@app.cls()` decorator, passing `gpu=None` here will not create a
692
+ CPU-only instance).
668
693
 
669
694
  **Usage:**
670
695
 
696
+ You can use this method after looking up the Cls from a deployed App or if you have a
697
+ direct reference to a Cls from another Function or local entrypoint on its App:
698
+
671
699
  ```python notest
672
700
  Model = modal.Cls.from_name("my_app", "Model")
673
701
  ModelUsingGPU = Model.with_options(gpu="A100")
674
- ModelUsingGPU().generate.remote(42) # will run with an A100 GPU
702
+ ModelUsingGPU().generate.remote(input_prompt) # Run with an A100 GPU
675
703
  ```
704
+
705
+ The method can be called multiple times to "stack" updates:
706
+
707
+ ```python notest
708
+ Model.with_options(gpu="A100").with_options(scaledown_window=300) # Use an A100 with slow scaledown
709
+ ```
710
+
711
+ Note that container arguments (i.e. `volumes` and `secrets`) passed in subsequent calls
712
+ will not be merged.
676
713
  """
677
714
  retry_policy = _parse_retries(retries, f"Class {self.__name__}" if self._user_cls else "")
678
715
  if gpu or cpu or memory:
@@ -705,21 +742,23 @@ More information on class parameterization can be found here: https://modal.com/
705
742
 
706
743
  cls = _Cls._from_loader(_load_from_base, rep=f"{self._name}.with_options(...)", is_another_app=True, deps=_deps)
707
744
  cls._initialize_from_other(self)
708
- cls._options = dataclasses.replace(
709
- cls._options,
745
+
746
+ new_options = _ServiceOptions(
710
747
  secrets=secrets,
748
+ validated_volumes=validate_volumes(volumes),
711
749
  resources=resources,
712
750
  retry_policy=retry_policy,
713
751
  max_containers=max_containers,
714
752
  buffer_containers=buffer_containers,
715
753
  scaledown_window=scaledown_window,
716
754
  timeout_secs=timeout,
717
- validated_volumes=validate_volumes(volumes),
718
755
  # Note: set both for backwards / forwards compatibility
719
756
  # But going forward `.with_concurrency` is the preferred method with distinct parameterization
720
757
  max_concurrent_inputs=allow_concurrent_inputs,
721
758
  target_concurrent_inputs=allow_concurrent_inputs,
722
759
  )
760
+
761
+ cls._options.merge_options(new_options)
723
762
  return cls
724
763
 
725
764
  def with_concurrency(self: "_Cls", *, max_inputs: int, target_inputs: Optional[int] = None) -> "_Cls":
@@ -746,9 +785,9 @@ More information on class parameterization can be found here: https://modal.com/
746
785
  _load_from_base, rep=f"{self._name}.with_concurrency(...)", is_another_app=True, deps=_deps
747
786
  )
748
787
  cls._initialize_from_other(self)
749
- cls._options = dataclasses.replace(
750
- cls._options, max_concurrent_inputs=max_inputs, target_concurrent_inputs=target_inputs
751
- )
788
+
789
+ concurrency_options = _ServiceOptions(max_concurrent_inputs=max_inputs, target_concurrent_inputs=target_inputs)
790
+ cls._options.merge_options(concurrency_options)
752
791
  return cls
753
792
 
754
793
  def with_batching(self: "_Cls", *, max_batch_size: int, wait_ms: int) -> "_Cls":
@@ -775,7 +814,9 @@ More information on class parameterization can be found here: https://modal.com/
775
814
  _load_from_base, rep=f"{self._name}.with_concurrency(...)", is_another_app=True, deps=_deps
776
815
  )
777
816
  cls._initialize_from_other(self)
778
- cls._options = dataclasses.replace(cls._options, batch_max_size=max_batch_size, batch_wait_ms=wait_ms)
817
+
818
+ batching_options = _ServiceOptions(batch_max_size=max_batch_size, batch_wait_ms=wait_ms)
819
+ cls._options.merge_options(batching_options)
779
820
  return cls
780
821
 
781
822
  @staticmethod
modal/cls.pyi CHANGED
@@ -37,6 +37,7 @@ class _ServiceOptions:
37
37
  batch_max_size: typing.Optional[int]
38
38
  batch_wait_ms: typing.Optional[int]
39
39
 
40
+ def merge_options(self, new_options: _ServiceOptions) -> _ServiceOptions: ...
40
41
  def __init__(
41
42
  self,
42
43
  secrets: typing.Collection[modal.secret._Secret] = (),
modal/parallel_map.py CHANGED
@@ -278,17 +278,19 @@ async def _map_invocation(
278
278
  )
279
279
  )
280
280
  map_done_task = asyncio.create_task(map_done_event.wait())
281
- done, pending = await asyncio.wait([get_response_task, map_done_task], return_when=FIRST_COMPLETED)
282
- if get_response_task in done:
283
- map_done_task.cancel()
284
- response = get_response_task.result()
285
- else:
286
- assert map_done_event.is_set()
287
- # map is done, cancel the pending call
281
+ try:
282
+ done, pending = await asyncio.wait([get_response_task, map_done_task], return_when=FIRST_COMPLETED)
283
+ if get_response_task in done:
284
+ map_done_task.cancel()
285
+ response = get_response_task.result()
286
+ else:
287
+ assert map_done_event.is_set()
288
+ # map is done - no more outputs, so return early
289
+ return
290
+ finally:
291
+ # clean up tasks, in case of cancellations etc.
288
292
  get_response_task.cancel()
289
- # not strictly necessary - don't leave dangling task
290
- await asyncio.gather(get_response_task, return_exceptions=True)
291
- return
293
+ map_done_task.cancel()
292
294
 
293
295
  last_entry_id = response.last_entry_id
294
296
  now_seconds = int(time.time())
@@ -716,6 +718,7 @@ class _MapItemContext:
716
718
  Return True if input state was changed to COMPLETE, otherwise False.
717
719
  """
718
720
  # If the item is already complete, this is a duplicate output and can be ignored.
721
+
719
722
  if self.state == _MapItemState.COMPLETE:
720
723
  logger.debug(
721
724
  f"Received output for input marked as complete. Must be duplicate, so ignoring. "
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.0.4.dev10
3
+ Version: 1.0.5.dev1
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -22,7 +22,7 @@ Requires-Dist: click~=8.1.0
22
22
  Requires-Dist: grpclib==0.4.7
23
23
  Requires-Dist: protobuf!=4.24.0,<7.0,>=3.19
24
24
  Requires-Dist: rich>=12.0.0
25
- Requires-Dist: synchronicity~=0.9.14
25
+ Requires-Dist: synchronicity~=0.9.15
26
26
  Requires-Dist: toml
27
27
  Requires-Dist: typer>=0.9
28
28
  Requires-Dist: types-certifi
@@ -22,11 +22,11 @@ modal/app.py,sha256=NZ_rJ9TuMfiNiLg8-gOFgufD5flGtXWPHOZI0gdD3hE,46585
22
22
  modal/app.pyi,sha256=4-b_vbe3lNAqQPcMRpQCEDsE1zsVkQRJGUql9B7HvbM,22659
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=OwISJvkgMb-rHm9Gc4i-7YcDgGiZgwJ7F_PzwZH7a6Q,16847
25
- modal/client.pyi,sha256=rX7aILLKNY7gU3DXvvGNT9Hi5IheqO6l0iFjSfDZMCw,8459
25
+ modal/client.pyi,sha256=mEQpk7s6oZXanSwdpeUki1HdXyxXk-Wu26jnOKGYDqY,8457
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=dBbeARwOWftlKd1cwtM0cHFtQWSWkwVXwVmOV4w0SyI,37907
29
- modal/cls.pyi,sha256=-gSV02T5S1La1dvmHHB_f139nNySk5MeXRSF4yp8z4s,12660
28
+ modal/cls.py,sha256=JjxAeVudXKMp-co9YUnsGG6COJOv91K0ylf4f3mR67o,39792
29
+ modal/cls.pyi,sha256=EL1mQuyf8nAL2AjVZVnZhDzhakUmQt1ZtOVMy_aYWJI,12742
30
30
  modal/config.py,sha256=e8sQ4RgwgJ_45S302vWUWs_wqRlKyEt3tU898RiaDKE,12073
31
31
  modal/container_process.py,sha256=PDvjcyZ6eeN8foKQgR0WJ66Sg3lt7OFhK7Y_Akz6k5w,5846
32
32
  modal/container_process.pyi,sha256=pPIUxVV_TY4huO2jF5cSSjb6L_EN7Es4xRvuwZ5sa5M,2802
@@ -52,7 +52,7 @@ modal/network_file_system.pyi,sha256=58DiUqHGlARmI3cz-Yo7IFObKKFIiGh5UIU5JxGNFfc
52
52
  modal/object.py,sha256=bTeskuY8JFrESjU4_UL_nTwYlBQdOLmVaOX3X6EMxsg,164
53
53
  modal/object.pyi,sha256=UkR8NQ1jCIaw3hBUPxBRc6vvrOqtV37G_hsW2O5-4wE,5378
54
54
  modal/output.py,sha256=q4T9uHduunj4NwY-YSwkHGgjZlCXMuJbfQ5UFaAGRAc,1968
55
- modal/parallel_map.py,sha256=1SOOUzKGVItv9fP2WvYR5vl49Y5q2GdupsnJMLIUDUw,37434
55
+ modal/parallel_map.py,sha256=QkDxaFQwzOCU6-Vhb-Iq78eEHKqqlNnGMqjk-BdhI08,37469
56
56
  modal/parallel_map.pyi,sha256=mhYGQmufQEJbjNrX7vNhBS2gUdfBrpmuWNUHth_Dz6U,6140
57
57
  modal/partial_function.py,sha256=SwuAAj2wj4SO6F6nkSnwNZrczEmm9w9YdlQTHh6hr04,1195
58
58
  modal/partial_function.pyi,sha256=NFWz1aCAs2B3-GnPf1cTatWRZOLnYpFKCnjP_X9iNRs,6411
@@ -91,7 +91,7 @@ modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5
91
91
  modal/_runtime/user_code_imports.py,sha256=78wJyleqY2RVibqcpbDQyfWVBVT9BjyHPeoV9WdwV5Y,17720
92
92
  modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
93
93
  modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
94
- modal/_utils/async_utils.py,sha256=wgOSjofVaQAyP8Oq_xTSET5mYP5Y5W_eaQCPmE63xRA,29321
94
+ modal/_utils/async_utils.py,sha256=MhSCsCL8GqIVFWoHubU_899IH-JBZAiiqadG9Wri2l4,29361
95
95
  modal/_utils/blob_utils.py,sha256=IexC2Jbtqp_Tkmy62ayfgzTYte0UPCNufB_v-DO21g8,18585
96
96
  modal/_utils/bytes_io_segment_payload.py,sha256=vaXPq8b52-x6G2hwE7SrjS58pg_aRm7gV3bn3yjmTzQ,4261
97
97
  modal/_utils/deprecation.py,sha256=EXP1beU4pmEqEzWMLw6E3kUfNfpmNA_VOp6i0EHi93g,4856
@@ -147,7 +147,7 @@ modal/requirements/2024.10.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddR
147
147
  modal/requirements/PREVIEW.txt,sha256=KxDaVTOwatHvboDo4lorlgJ7-n-MfAwbPwxJ0zcJqrs,312
148
148
  modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
149
149
  modal/requirements/base-images.json,sha256=f1bwyp2UkM844eoO9Qk30gQw_xrMqKpMSeJ6MErXnEk,995
150
- modal-1.0.4.dev10.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
150
+ modal-1.0.5.dev1.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
151
151
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
152
152
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
153
153
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -155,10 +155,10 @@ modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,2
155
155
  modal_docs/mdmd/mdmd.py,sha256=Irx49MCCTlBOP4FBdLR--JrpA3-WhsVeriq0LGgsRic,6232
156
156
  modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
157
157
  modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
158
- modal_proto/api.proto,sha256=nwh8RmhypmnFMKQ-WwHolz-1NvKr4JgRSc2As_cZ0JE,96407
158
+ modal_proto/api.proto,sha256=aBHQSEFm1Os9ciCl6N9N5bhumjdJIpl7VPSbP2Chjk4,96432
159
159
  modal_proto/api_grpc.py,sha256=iY5o_Tm4VDP-Wa1JgA_NpQa_Y-4FYB_RN9wdSUExjwI,117469
160
- modal_proto/api_pb2.py,sha256=UeQg7ABeXbNCBLcft3pTZtV6EoLMifZ_O_BPxYadm0w,338603
161
- modal_proto/api_pb2.pyi,sha256=QQp5_zlv3j6_v7CaqM4yFKW4xun1yzy4lxwkn4_O3Ew,463819
160
+ modal_proto/api_pb2.py,sha256=PTGsaddASU8iqRoaTFmV5bNg-TtGGUGvf8tTVWjeR1Q,338641
161
+ modal_proto/api_pb2.pyi,sha256=hMvUMcYnpt7tR0fdkL-sDuOM-r2vistuds_rL03xY-k,463959
162
162
  modal_proto/api_pb2_grpc.py,sha256=NL5prOS_hh_pA1hVvQP_ZRE1w49N-PR8iNPRZ65i6nA,254089
163
163
  modal_proto/api_pb2_grpc.pyi,sha256=Xxgdcnv1mBnu5_AQxJ6fo0yz7GnqVU0HVObNfZWHVfM,59440
164
164
  modal_proto/modal_api_grpc.py,sha256=0ir2lnwT3-IgPcAWw98yWMAiqZPkjvNro9UBk4u8hnk,17763
@@ -170,10 +170,10 @@ modal_proto/options_pb2.pyi,sha256=l7DBrbLO7q3Ir-XDkWsajm0d0TQqqrfuX54i4BMpdQg,1
170
170
  modal_proto/options_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
171
171
  modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0yJSI,247
172
172
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
- modal_version/__init__.py,sha256=LNXQQhlCrbhgsIePbjkDp4B4zhO1mSW6NEDtVxtRmu4,121
173
+ modal_version/__init__.py,sha256=IBJQ2mcZZY6WLV44CaQFHUQqyZlvclRpvrY6CtGf5gs,120
174
174
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
175
- modal-1.0.4.dev10.dist-info/METADATA,sha256=4tXSMwgkVzvte0dFfa9VIzNOYw3u0L4DGTh5oDHfw8U,2455
176
- modal-1.0.4.dev10.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
177
- modal-1.0.4.dev10.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
178
- modal-1.0.4.dev10.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
179
- modal-1.0.4.dev10.dist-info/RECORD,,
175
+ modal-1.0.5.dev1.dist-info/METADATA,sha256=HkVebF9LFr4MAW7KocR0W-AVfGfw5U6eUiU5gxTPPHI,2454
176
+ modal-1.0.5.dev1.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
177
+ modal-1.0.5.dev1.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
178
+ modal-1.0.5.dev1.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
179
+ modal-1.0.5.dev1.dist-info/RECORD,,
modal_proto/api.proto CHANGED
@@ -2796,6 +2796,7 @@ message TaskInfo {
2796
2796
  modal.client.GenericResult result = 4;
2797
2797
  double enqueued_at = 5;
2798
2798
  string gpu_type = 6;
2799
+ string sandbox_id = 7;
2799
2800
  }
2800
2801
 
2801
2802
  message TaskListRequest {