modal 1.0.4.dev3__py3-none-any.whl → 1.0.4.dev5__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.
@@ -647,7 +647,9 @@ class StopSentinelType: ...
647
647
  STOP_SENTINEL = StopSentinelType()
648
648
 
649
649
 
650
- async def async_merge(*generators: AsyncGenerator[T, None]) -> AsyncGenerator[T, None]:
650
+ async def async_merge(
651
+ *generators: AsyncGenerator[T, None], cancellation_timeout: float = 10.0
652
+ ) -> AsyncGenerator[T, None]:
651
653
  """
652
654
  Asynchronously merges multiple async generators into a single async generator.
653
655
 
@@ -692,8 +694,9 @@ async def async_merge(*generators: AsyncGenerator[T, None]) -> AsyncGenerator[T,
692
694
 
693
695
  async def producer(generator: AsyncGenerator[T, None]):
694
696
  try:
695
- async for item in generator:
696
- await queue.put(ValueWrapper(item))
697
+ async with aclosing(generator) as stream:
698
+ async for item in stream:
699
+ await queue.put(ValueWrapper(item))
697
700
  except Exception as e:
698
701
  await queue.put(ExceptionWrapper(e))
699
702
 
@@ -735,15 +738,20 @@ async def async_merge(*generators: AsyncGenerator[T, None]) -> AsyncGenerator[T,
735
738
  new_output_task = asyncio.create_task(queue.get())
736
739
 
737
740
  finally:
738
- if not new_output_task.done():
739
- new_output_task.cancel()
740
- for task in tasks:
741
- if not task.done():
742
- try:
743
- task.cancel()
744
- await task
745
- except asyncio.CancelledError:
746
- pass
741
+ unfinished_tasks = [t for t in tasks | {new_output_task} if not t.done()]
742
+ for t in unfinished_tasks:
743
+ t.cancel()
744
+ try:
745
+ await asyncio.wait_for(
746
+ asyncio.shield(
747
+ # we need to `shield` the `gather` to ensure cooperation with the timeout
748
+ # all underlying tasks have been marked as cancelled at this point anyway
749
+ asyncio.gather(*unfinished_tasks, return_exceptions=True)
750
+ ),
751
+ timeout=cancellation_timeout,
752
+ )
753
+ except asyncio.TimeoutError:
754
+ logger.debug("Timed out while cleaning up async_merge")
747
755
 
748
756
 
749
757
  async def callable_to_agen(awaitable: Callable[[], Awaitable[T]]) -> AsyncGenerator[T, None]:
@@ -761,16 +769,34 @@ async def gather_cancel_on_exc(*coros_or_futures):
761
769
  raise
762
770
 
763
771
 
772
+ async def prevent_cancellation_abortion(coro):
773
+ # if this is cancelled, it will wait for coro cancellation handling
774
+ # and then unconditionally re-raises a CancelledError, even if the underlying coro
775
+ # doesn't re-raise the cancellation itself
776
+ t = asyncio.create_task(coro)
777
+ try:
778
+ return await asyncio.shield(t)
779
+ except asyncio.CancelledError:
780
+ if t.cancelled():
781
+ # coro cancelled itself - reraise
782
+ raise
783
+ t.cancel() # cancel task
784
+ await t # this *normally* reraises
785
+ raise # if the above somehow resolved, by swallowing cancellation - we still raise
786
+
787
+
764
788
  async def async_map(
765
789
  input_generator: AsyncGenerator[T, None],
766
790
  async_mapper_func: Callable[[T], Awaitable[V]],
767
791
  concurrency: int,
792
+ cancellation_timeout: float = 10.0,
768
793
  ) -> AsyncGenerator[V, None]:
769
794
  queue: asyncio.Queue[Union[ValueWrapper[T], StopSentinelType]] = asyncio.Queue(maxsize=concurrency * 2)
770
795
 
771
796
  async def producer() -> AsyncGenerator[V, None]:
772
- async for item in input_generator:
773
- await queue.put(ValueWrapper(item))
797
+ async with aclosing(input_generator) as stream:
798
+ async for item in stream:
799
+ await queue.put(ValueWrapper(item))
774
800
 
775
801
  for _ in range(concurrency):
776
802
  await queue.put(STOP_SENTINEL)
@@ -784,14 +810,17 @@ async def async_map(
784
810
  while True:
785
811
  item = await queue.get()
786
812
  if isinstance(item, ValueWrapper):
787
- yield await async_mapper_func(item.value)
813
+ res = await prevent_cancellation_abortion(async_mapper_func(item.value))
814
+ yield res
788
815
  elif isinstance(item, ExceptionWrapper):
789
816
  raise item.value
790
817
  else:
791
818
  assert_type(item, StopSentinelType)
792
819
  break
793
820
 
794
- async with aclosing(async_merge(*[worker() for _ in range(concurrency)], producer())) as stream:
821
+ async with aclosing(
822
+ async_merge(*[worker() for i in range(concurrency)], producer(), cancellation_timeout=cancellation_timeout)
823
+ ) as stream:
795
824
  async for item in stream:
796
825
  yield item
797
826
 
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.dev3",
34
+ version: str = "1.0.4.dev5",
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.dev3",
97
+ version: str = "1.0.4.dev5",
98
98
  ): ...
99
99
  def is_closed(self) -> bool: ...
100
100
  @property
modal/sandbox.py CHANGED
@@ -105,6 +105,7 @@ class _Sandbox(_Object, type_prefix="sb"):
105
105
  proxy: Optional[_Proxy] = None,
106
106
  _experimental_scheduler_placement: Optional[SchedulerPlacement] = None,
107
107
  enable_snapshot: bool = False,
108
+ verbose: bool = False,
108
109
  ) -> "_Sandbox":
109
110
  """mdmd:hidden"""
110
111
 
@@ -205,6 +206,7 @@ class _Sandbox(_Object, type_prefix="sb"):
205
206
  network_access=network_access,
206
207
  proxy_id=(proxy.object_id if proxy else None),
207
208
  enable_snapshot=enable_snapshot,
209
+ verbose=verbose,
208
210
  )
209
211
 
210
212
  # Note - `resolver.app_id` will be `None` for app-less sandboxes
@@ -253,6 +255,8 @@ class _Sandbox(_Object, type_prefix="sb"):
253
255
  unencrypted_ports: Sequence[int] = [],
254
256
  # Reference to a Modal Proxy to use in front of this Sandbox.
255
257
  proxy: Optional[_Proxy] = None,
258
+ # Enable verbose logging for sandbox operations.
259
+ verbose: bool = False,
256
260
  # Enable memory snapshots.
257
261
  _experimental_enable_snapshot: bool = False,
258
262
  _experimental_scheduler_placement: Optional[
@@ -298,6 +302,7 @@ class _Sandbox(_Object, type_prefix="sb"):
298
302
  _experimental_enable_snapshot=_experimental_enable_snapshot,
299
303
  _experimental_scheduler_placement=_experimental_scheduler_placement,
300
304
  client=client,
305
+ verbose=verbose,
301
306
  )
302
307
 
303
308
  @staticmethod
@@ -342,6 +347,7 @@ class _Sandbox(_Object, type_prefix="sb"):
342
347
  SchedulerPlacement
343
348
  ] = None, # Experimental controls over fine-grained scheduling (alpha).
344
349
  client: Optional[_Client] = None,
350
+ verbose: bool = False,
345
351
  ):
346
352
  # This method exposes some internal arguments (currently `mounts`) which are not in the public API
347
353
  # `mounts` is currently only used by modal shell (cli) to provide a function's mounts to the
@@ -376,6 +382,7 @@ class _Sandbox(_Object, type_prefix="sb"):
376
382
  proxy=proxy,
377
383
  _experimental_scheduler_placement=_experimental_scheduler_placement,
378
384
  enable_snapshot=_experimental_enable_snapshot,
385
+ verbose=verbose,
379
386
  )
380
387
  obj._enable_snapshot = _experimental_enable_snapshot
381
388
 
modal/sandbox.pyi CHANGED
@@ -63,6 +63,7 @@ class _Sandbox(modal._object._Object):
63
63
  proxy: typing.Optional[modal.proxy._Proxy] = None,
64
64
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
65
65
  enable_snapshot: bool = False,
66
+ verbose: bool = False,
66
67
  ) -> _Sandbox: ...
67
68
  @staticmethod
68
69
  async def create(
@@ -90,6 +91,7 @@ class _Sandbox(modal._object._Object):
90
91
  h2_ports: collections.abc.Sequence[int] = [],
91
92
  unencrypted_ports: collections.abc.Sequence[int] = [],
92
93
  proxy: typing.Optional[modal.proxy._Proxy] = None,
94
+ verbose: bool = False,
93
95
  _experimental_enable_snapshot: bool = False,
94
96
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
95
97
  client: typing.Optional[modal.client._Client] = None,
@@ -124,6 +126,7 @@ class _Sandbox(modal._object._Object):
124
126
  _experimental_enable_snapshot: bool = False,
125
127
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
126
128
  client: typing.Optional[modal.client._Client] = None,
129
+ verbose: bool = False,
127
130
  ): ...
128
131
  def _hydrate_metadata(self, handle_metadata: typing.Optional[google.protobuf.message.Message]): ...
129
132
  @staticmethod
@@ -236,6 +239,7 @@ class Sandbox(modal.object.Object):
236
239
  proxy: typing.Optional[modal.proxy.Proxy] = None,
237
240
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
238
241
  enable_snapshot: bool = False,
242
+ verbose: bool = False,
239
243
  ) -> Sandbox: ...
240
244
 
241
245
  class __create_spec(typing_extensions.Protocol):
@@ -268,6 +272,7 @@ class Sandbox(modal.object.Object):
268
272
  h2_ports: collections.abc.Sequence[int] = [],
269
273
  unencrypted_ports: collections.abc.Sequence[int] = [],
270
274
  proxy: typing.Optional[modal.proxy.Proxy] = None,
275
+ verbose: bool = False,
271
276
  _experimental_enable_snapshot: bool = False,
272
277
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
273
278
  client: typing.Optional[modal.client.Client] = None,
@@ -301,6 +306,7 @@ class Sandbox(modal.object.Object):
301
306
  h2_ports: collections.abc.Sequence[int] = [],
302
307
  unencrypted_ports: collections.abc.Sequence[int] = [],
303
308
  proxy: typing.Optional[modal.proxy.Proxy] = None,
309
+ verbose: bool = False,
304
310
  _experimental_enable_snapshot: bool = False,
305
311
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
306
312
  client: typing.Optional[modal.client.Client] = None,
@@ -342,6 +348,7 @@ class Sandbox(modal.object.Object):
342
348
  _experimental_enable_snapshot: bool = False,
343
349
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
344
350
  client: typing.Optional[modal.client.Client] = None,
351
+ verbose: bool = False,
345
352
  ): ...
346
353
  async def aio(
347
354
  self,
@@ -376,6 +383,7 @@ class Sandbox(modal.object.Object):
376
383
  _experimental_enable_snapshot: bool = False,
377
384
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
378
385
  client: typing.Optional[modal.client.Client] = None,
386
+ verbose: bool = False,
379
387
  ): ...
380
388
 
381
389
  _create: ___create_spec
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.0.4.dev3
3
+ Version: 1.0.4.dev5
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.13
25
+ Requires-Dist: synchronicity~=0.9.14
26
26
  Requires-Dist: toml
27
27
  Requires-Dist: typer>=0.9
28
28
  Requires-Dist: types-certifi
@@ -22,7 +22,7 @@ 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=Usjx0mMca3mv2yk7gG7KRk5f0UYBOoTjT6mFzJNXWBc,8457
25
+ modal/client.pyi,sha256=xTpYHIPiyvBSFn8jeYF4fxZTT9RWA5rvy07BsIX3YAQ,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
28
  modal/cls.py,sha256=dBbeARwOWftlKd1cwtM0cHFtQWSWkwVXwVmOV4w0SyI,37907
@@ -65,8 +65,8 @@ modal/retries.py,sha256=IvNLDM0f_GLUDD5VgEDoN09C88yoxSrCquinAuxT1Sc,5205
65
65
  modal/runner.py,sha256=VV-PC03waAdSc_tAwpVN427TelOgs-cKeYS2GFeVRuA,24029
66
66
  modal/runner.pyi,sha256=1AnEu48SUPnLWp3raQ2zJCV5lc85EGLkX2nL0bHWaB0,5162
67
67
  modal/running_app.py,sha256=v61mapYNV1-O-Uaho5EfJlryMLvIT9We0amUOSvSGx8,1188
68
- modal/sandbox.py,sha256=6Ki2aBANqqN7THC1U7ymzJEUN7bPRqixAX9TBFh05Mc,36250
69
- modal/sandbox.pyi,sha256=KvIYfLIvWdTEbuu0oWoS2I_8UiT6HU2hNsEaS7AjFR4,28688
68
+ modal/sandbox.py,sha256=j4uQXiXPNKmQ_FyvzT9W9bWsElJOp2Vl--rBqszmCis,36491
69
+ modal/sandbox.pyi,sha256=2kCltK5PZwwYBJo70vfV4Ja8Vv_T9BBWJT18am4XA0c,28952
70
70
  modal/schedule.py,sha256=SdH8jk6S0zoc1bTRVblrVw0zBsNwPlSC2gNpVxMet9g,3061
71
71
  modal/scheduler_placement.py,sha256=BAREdOY5HzHpzSBqt6jDVR6YC_jYfHMVqOzkyqQfngU,1235
72
72
  modal/secret.py,sha256=I2z-rgKWl_Ix107d2_Y2OWGXdFOuJ7zMOyDfIOdFI1A,10374
@@ -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=zjdtdA54zvNL_RuREmN5NWFhhiRcNh8z0jT2rBc5RgY,28001
94
+ modal/_utils/async_utils.py,sha256=wgOSjofVaQAyP8Oq_xTSET5mYP5Y5W_eaQCPmE63xRA,29321
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.dev3.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
150
+ modal-1.0.4.dev5.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=W-cvTWRT5LWw6FvfZUWGORQMEkUV4hqBpmbDD24N3C4,96314
158
+ modal_proto/api.proto,sha256=nwh8RmhypmnFMKQ-WwHolz-1NvKr4JgRSc2As_cZ0JE,96407
159
159
  modal_proto/api_grpc.py,sha256=iY5o_Tm4VDP-Wa1JgA_NpQa_Y-4FYB_RN9wdSUExjwI,117469
160
- modal_proto/api_pb2.py,sha256=C0eUCmX2r7X7UgXkNkK30zA8GqQE9JgzShaJz_eay8Q,338564
161
- modal_proto/api_pb2.pyi,sha256=51bFLZW74Cy5F4wEm36ZXHKfFht-cfB4RCre4WWAteo,463616
160
+ modal_proto/api_pb2.py,sha256=UeQg7ABeXbNCBLcft3pTZtV6EoLMifZ_O_BPxYadm0w,338603
161
+ modal_proto/api_pb2.pyi,sha256=QQp5_zlv3j6_v7CaqM4yFKW4xun1yzy4lxwkn4_O3Ew,463819
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=OzYkTqYrKPayFBpy1hn7qm_kTkaXk-Pl3QXmSPZD7GQ,120
173
+ modal_version/__init__.py,sha256=rEupqoZqs1KAZkOmUF7JeuZEdb0uwFFt526TxAse5os,120
174
174
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
175
- modal-1.0.4.dev3.dist-info/METADATA,sha256=0uYQfdEZBMCQj_44tC4ETQtmujVmq5P_GX0X8joqcS8,2454
176
- modal-1.0.4.dev3.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
177
- modal-1.0.4.dev3.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
178
- modal-1.0.4.dev3.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
179
- modal-1.0.4.dev3.dist-info/RECORD,,
175
+ modal-1.0.4.dev5.dist-info/METADATA,sha256=HBT9adM3iflD0F_3NEzpwdnO-ZviiyrH72UB5hBsLDg,2454
176
+ modal-1.0.4.dev5.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
177
+ modal-1.0.4.dev5.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
178
+ modal-1.0.4.dev5.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
179
+ modal-1.0.4.dev5.dist-info/RECORD,,
modal_proto/api.proto CHANGED
@@ -2440,6 +2440,9 @@ message Sandbox {
2440
2440
 
2441
2441
  // If set, overrides the runtime used by the function, either "runc" or "gvisor".
2442
2442
  optional string runtime = 28;
2443
+
2444
+ // If set, the sandbox will be created with verbose logging enabled.
2445
+ bool verbose = 29;
2443
2446
  }
2444
2447
 
2445
2448
  message SandboxCreateRequest {