modal 0.67.1__py3-none-any.whl → 0.67.6__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/_runtime/asgi.py CHANGED
@@ -280,9 +280,48 @@ def wait_for_web_server(host: str, port: int, *, timeout: float) -> None:
280
280
  ) from ex
281
281
 
282
282
 
283
+ def _add_forwarded_for_header(scope):
284
+ # we strip X-Forwarded-For headers from the scope
285
+ # but we can add it back from the ASGI scope
286
+ # https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope
287
+
288
+ # X-Forwarded-For headers is a comma separated list of IP addresses
289
+ # there may be multiple X-Forwarded-For headers
290
+ # we want to prepend the client IP to the first one
291
+ # but only if it doesn't exist in one of the headers already
292
+
293
+ first_x_forwarded_for_idx = None
294
+ if "headers" in scope and "client" in scope:
295
+ client_host = scope["client"][0]
296
+
297
+ for idx, header in enumerate(scope["headers"]):
298
+ if header[0] == b"X-Forwarded-For":
299
+ if first_x_forwarded_for_idx is None:
300
+ first_x_forwarded_for_idx = idx
301
+ values = header[1].decode().split(", ")
302
+
303
+ if client_host in values:
304
+ # we already have the client IP in this header
305
+ # return early
306
+ return scope
307
+
308
+ if first_x_forwarded_for_idx is not None:
309
+ # we have X-Forwarded-For headers but they don't have the client IP
310
+ # we need to prepend the client IP to the first one
311
+ values = [client_host] + scope["headers"][first_x_forwarded_for_idx][1].decode().split(", ")
312
+ scope["headers"][first_x_forwarded_for_idx] = (b"X-Forwarded-For", ", ".join(values).encode())
313
+ else:
314
+ # we don't have X-Forwarded-For headers, we need to add one
315
+ scope["headers"].append((b"X-Forwarded-For", client_host.encode()))
316
+
317
+ return scope
318
+
319
+
283
320
  async def _proxy_http_request(session: aiohttp.ClientSession, scope, receive, send) -> None:
284
321
  proxy_response: aiohttp.ClientResponse
285
322
 
323
+ scope = _add_forwarded_for_header(scope)
324
+
286
325
  async def request_generator() -> AsyncGenerator[bytes, None]:
287
326
  while True:
288
327
  message = await receive()
modal/_tunnel.py CHANGED
@@ -61,7 +61,7 @@ async def _forward(port: int, *, unencrypted: bool = False, client: Optional[_Cl
61
61
 
62
62
  **Usage:**
63
63
 
64
- ```python
64
+ ```python notest
65
65
  import modal
66
66
  from flask import Flask
67
67
 
@@ -573,6 +573,46 @@ STOP_SENTINEL = StopSentinelType()
573
573
 
574
574
 
575
575
  async def async_merge(*generators: AsyncGenerator[T, None]) -> AsyncGenerator[T, None]:
576
+ """
577
+ Asynchronously merges multiple async generators into a single async generator.
578
+
579
+ This function takes multiple async generators and yields their values in the order
580
+ they are produced. If any generator raises an exception, the exception is propagated.
581
+
582
+ Args:
583
+ *generators: One or more async generators to be merged.
584
+
585
+ Yields:
586
+ The values produced by the input async generators.
587
+
588
+ Raises:
589
+ Exception: If any of the input generators raises an exception, it is propagated.
590
+
591
+ Usage:
592
+ ```python
593
+ import asyncio
594
+ from modal._utils.async_utils import async_merge
595
+
596
+ async def gen1():
597
+ yield 1
598
+ yield 2
599
+
600
+ async def gen2():
601
+ yield "a"
602
+ yield "b"
603
+
604
+ async def example():
605
+ values = set()
606
+ async for value in async_merge(gen1(), gen2()):
607
+ values.add(value)
608
+
609
+ return values
610
+
611
+ # Output could be: {1, "a", 2, "b"} (order may vary)
612
+ values = asyncio.run(example())
613
+ assert values == {1, "a", 2, "b"}
614
+ ```
615
+ """
576
616
  queue: asyncio.Queue[Union[ValueWrapper[T], ExceptionWrapper]] = asyncio.Queue(maxsize=len(generators) * 10)
577
617
 
578
618
  async def producer(generator: AsyncGenerator[T, None]):
modal/app.py CHANGED
@@ -313,23 +313,6 @@ class _App:
313
313
  if not isinstance(value, _Object):
314
314
  raise InvalidError(f"App attribute `{key}` with value {value!r} is not a valid Modal object")
315
315
 
316
- def _add_object(self, tag, obj):
317
- # TODO(erikbern): replace this with _add_function and _add_class
318
- if self._running_app:
319
- # If this is inside a container, then objects can be defined after app initialization.
320
- # So we may have to initialize objects once they get bound to the app.
321
- if tag in self._running_app.tag_to_object_id:
322
- object_id: str = self._running_app.tag_to_object_id[tag]
323
- metadata: Message = self._running_app.object_handle_metadata[object_id]
324
- obj._hydrate(object_id, self._client, metadata)
325
-
326
- if isinstance(obj, _Function):
327
- self._functions[tag] = obj
328
- elif isinstance(obj, _Cls):
329
- self._classes[tag] = obj
330
- else:
331
- raise RuntimeError(f"Expected `obj` to be a _Function or _Cls (got {type(obj)}")
332
-
333
316
  def __getitem__(self, tag: str):
334
317
  deprecation_error((2024, 3, 25), _app_attr_error)
335
318
 
@@ -401,14 +384,14 @@ class _App:
401
384
 
402
385
  **Example**
403
386
 
404
- ```python
387
+ ```python notest
405
388
  with app.run():
406
389
  some_modal_function.remote()
407
390
  ```
408
391
 
409
392
  To enable output printing, use `modal.enable_output()`:
410
393
 
411
- ```python
394
+ ```python notest
412
395
  with modal.enable_output():
413
396
  with app.run():
414
397
  some_modal_function.remote()
@@ -481,10 +464,29 @@ class _App:
481
464
  if function.tag in self._classes:
482
465
  logger.warning(f"Warning: tag {function.tag} exists but is overridden by function")
483
466
 
484
- self._add_object(function.tag, function)
467
+ if self._running_app:
468
+ # If this is inside a container, then objects can be defined after app initialization.
469
+ # So we may have to initialize objects once they get bound to the app.
470
+ if function.tag in self._running_app.tag_to_object_id:
471
+ object_id: str = self._running_app.tag_to_object_id[function.tag]
472
+ metadata: Message = self._running_app.object_handle_metadata[object_id]
473
+ function._hydrate(object_id, self._client, metadata)
474
+
475
+ self._functions[function.tag] = function
485
476
  if is_web_endpoint:
486
477
  self._web_endpoints.append(function.tag)
487
478
 
479
+ def _add_class(self, tag: str, cls: _Cls):
480
+ if self._running_app:
481
+ # If this is inside a container, then objects can be defined after app initialization.
482
+ # So we may have to initialize objects once they get bound to the app.
483
+ if tag in self._running_app.tag_to_object_id:
484
+ object_id: str = self._running_app.tag_to_object_id[tag]
485
+ metadata: Message = self._running_app.object_handle_metadata[object_id]
486
+ cls._hydrate(object_id, self._client, metadata)
487
+
488
+ self._classes[tag] = cls
489
+
488
490
  def _init_container(self, client: _Client, running_app: RunningApp):
489
491
  self._app_id = running_app.app_id
490
492
  self._running_app = running_app
@@ -935,7 +937,7 @@ class _App:
935
937
  cls: _Cls = _Cls.from_local(user_cls, self, cls_func)
936
938
 
937
939
  tag: str = user_cls.__name__
938
- self._add_object(tag, cls)
940
+ self._add_class(tag, cls)
939
941
  return cls # type: ignore # a _Cls instance "simulates" being the user provided class
940
942
 
941
943
  return wrapper
@@ -1022,16 +1024,25 @@ class _App:
1022
1024
  bar.remote()
1023
1025
  ```
1024
1026
  """
1025
- indexed_objects = dict(**other_app._functions, **other_app._classes)
1026
- for tag, object in indexed_objects.items():
1027
- existing_object = indexed_objects.get(tag)
1028
- if existing_object and existing_object != object:
1027
+ for tag, function in other_app._functions.items():
1028
+ existing_function = self._functions.get(tag)
1029
+ if existing_function and existing_function != function:
1030
+ logger.warning(
1031
+ f"Named app function {tag} with existing value {existing_function} is being "
1032
+ f"overwritten by a different function {function}"
1033
+ )
1034
+
1035
+ self._add_function(function, False) # TODO(erikbern): webhook config?
1036
+
1037
+ for tag, cls in other_app._classes.items():
1038
+ existing_cls = self._classes.get(tag)
1039
+ if existing_cls and existing_cls != cls:
1029
1040
  logger.warning(
1030
- f"Named app object {tag} with existing value {existing_object} is being "
1031
- f"overwritten by a different object {object}"
1041
+ f"Named app class {tag} with existing value {existing_cls} is being "
1042
+ f"overwritten by a different class {cls}"
1032
1043
  )
1033
1044
 
1034
- self._add_object(tag, object)
1045
+ self._add_class(tag, cls)
1035
1046
 
1036
1047
  async def _logs(self, client: Optional[_Client] = None) -> AsyncGenerator[str, None]:
1037
1048
  """Stream logs from the app.
modal/app.pyi CHANGED
@@ -114,7 +114,6 @@ class _App:
114
114
  ) -> _App: ...
115
115
  def set_description(self, description: str): ...
116
116
  def _validate_blueprint_value(self, key: str, value: typing.Any): ...
117
- def _add_object(self, tag, obj): ...
118
117
  def __getitem__(self, tag: str): ...
119
118
  def __setitem__(self, tag: str, obj: modal.object._Object): ...
120
119
  def __getattr__(self, tag: str): ...
@@ -137,6 +136,7 @@ class _App:
137
136
  def _get_default_image(self): ...
138
137
  def _get_watch_mounts(self): ...
139
138
  def _add_function(self, function: modal.functions._Function, is_web_endpoint: bool): ...
139
+ def _add_class(self, tag: str, cls: modal.cls._Cls): ...
140
140
  def _init_container(self, client: modal.client._Client, running_app: modal.running_app.RunningApp): ...
141
141
  @property
142
142
  def registered_functions(self) -> typing.Dict[str, modal.functions._Function]: ...
@@ -321,7 +321,6 @@ class App:
321
321
 
322
322
  def set_description(self, description: str): ...
323
323
  def _validate_blueprint_value(self, key: str, value: typing.Any): ...
324
- def _add_object(self, tag, obj): ...
325
324
  def __getitem__(self, tag: str): ...
326
325
  def __setitem__(self, tag: str, obj: modal.object.Object): ...
327
326
  def __getattr__(self, tag: str): ...
@@ -363,6 +362,7 @@ class App:
363
362
  def _get_default_image(self): ...
364
363
  def _get_watch_mounts(self): ...
365
364
  def _add_function(self, function: modal.functions.Function, is_web_endpoint: bool): ...
365
+ def _add_class(self, tag: str, cls: modal.cls.Cls): ...
366
366
  def _init_container(self, client: modal.client.Client, running_app: modal.running_app.RunningApp): ...
367
367
  @property
368
368
  def registered_functions(self) -> typing.Dict[str, modal.functions.Function]: ...
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[typing.Tuple[str, str]],
34
- version: str = "0.67.1",
34
+ version: str = "0.67.6",
35
35
  ): ...
36
36
  def is_closed(self) -> bool: ...
37
37
  @property
@@ -90,7 +90,7 @@ class Client:
90
90
  server_url: str,
91
91
  client_type: int,
92
92
  credentials: typing.Optional[typing.Tuple[str, str]],
93
- version: str = "0.67.1",
93
+ version: str = "0.67.6",
94
94
  ): ...
95
95
  def is_closed(self) -> bool: ...
96
96
  @property
modal/cls.py CHANGED
@@ -154,7 +154,7 @@ class _Obj:
154
154
  Note that all Modal methods and web endpoints of a class share the same set
155
155
  of containers and the warm_pool_size affects that common container pool.
156
156
 
157
- ```python
157
+ ```python notest
158
158
  # Usage on a parametrized function.
159
159
  Model = modal.Cls.lookup("my-app", "Model")
160
160
  Model("fine-tuned-model").keep_warm(2)
@@ -524,7 +524,7 @@ class _Cls(_Object, type_prefix="cs"):
524
524
  In contrast to `modal.Cls.from_name`, this is an eager method
525
525
  that will hydrate the local object with metadata from Modal servers.
526
526
 
527
- ```python
527
+ ```python notest
528
528
  Class = modal.Cls.lookup("other-app", "Class")
529
529
  obj = Class()
530
530
  ```
modal/dict.py CHANGED
@@ -88,7 +88,9 @@ class _Dict(_Object, type_prefix="di"):
88
88
 
89
89
  with Dict.ephemeral() as d:
90
90
  d["foo"] = "bar"
91
+ ```
91
92
 
93
+ ```python notest
92
94
  async with Dict.ephemeral() as d:
93
95
  await d.put.aio("foo", "bar")
94
96
  ```
modal/functions.py CHANGED
@@ -997,7 +997,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
997
997
  Please exercise care when using this advanced feature!
998
998
  Setting and forgetting a warm pool on functions can lead to increased costs.
999
999
 
1000
- ```python
1000
+ ```python notest
1001
1001
  # Usage on a regular function.
1002
1002
  f = modal.Function.lookup("my-app", "function")
1003
1003
  f.keep_warm(2)
@@ -1076,7 +1076,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1076
1076
  In contrast to `modal.Function.from_name`, this is an eager method
1077
1077
  that will hydrate the local object with metadata from Modal servers.
1078
1078
 
1079
- ```python
1079
+ ```python notest
1080
1080
  f = modal.Function.lookup("other-app", "function")
1081
1081
  ```
1082
1082
  """
modal/functions.pyi CHANGED
@@ -436,11 +436,11 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
436
436
 
437
437
  _call_generator_nowait: ___call_generator_nowait_spec
438
438
 
439
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
439
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
440
440
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
441
441
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
442
442
 
443
- remote: __remote_spec[ReturnType, P]
443
+ remote: __remote_spec[P, ReturnType]
444
444
 
445
445
  class __remote_gen_spec(typing_extensions.Protocol):
446
446
  def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
@@ -452,17 +452,17 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
452
452
  def _get_obj(self) -> typing.Optional[modal.cls.Obj]: ...
453
453
  def local(self, *args: P.args, **kwargs: P.kwargs) -> OriginalReturnType: ...
454
454
 
455
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
455
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
456
456
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
457
457
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
458
458
 
459
- _experimental_spawn: ___experimental_spawn_spec[ReturnType, P]
459
+ _experimental_spawn: ___experimental_spawn_spec[P, ReturnType]
460
460
 
461
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
461
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
462
462
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
463
463
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
464
464
 
465
- spawn: __spawn_spec[ReturnType, P]
465
+ spawn: __spawn_spec[P, ReturnType]
466
466
 
467
467
  def get_raw_f(self) -> typing.Callable[..., typing.Any]: ...
468
468
 
modal/image.py CHANGED
@@ -375,7 +375,7 @@ class _Image(_Object, type_prefix="im"):
375
375
  "\n"
376
376
  "Run `image.add_local_*` commands last in your image build to avoid rebuilding images with every local "
377
377
  "file change. Modal will then add these files to containers on startup instead, saving build time.\n"
378
- "If you need to run other build steps after adding local files, set `copy=True` to copy the files"
378
+ "If you need to run other build steps after adding local files, set `copy=True` to copy the files "
379
379
  "directly into the image, at the expense of some added build time.\n"
380
380
  "\n"
381
381
  "Example:\n"
modal/io_streams.py CHANGED
@@ -385,13 +385,14 @@ class _StreamWriter:
385
385
 
386
386
  **Usage**
387
387
 
388
- ```python
389
- # Synchronous
388
+ ```python notest
390
389
  writer.write(data)
391
390
  writer.drain()
391
+ ```
392
392
 
393
- # Async
394
- writer.write(data)
393
+ Async usage:
394
+ ```python notest
395
+ writer.write(data) # not a blocking operation
395
396
  await writer.drain.aio()
396
397
  ```
397
398
  """
@@ -152,11 +152,13 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
152
152
 
153
153
  Usage:
154
154
  ```python
155
- with NetworkFileSystem.ephemeral() as nfs:
156
- assert nfs.listdir() == []
155
+ with modal.NetworkFileSystem.ephemeral() as nfs:
156
+ assert nfs.listdir("/") == []
157
+ ```
157
158
 
158
- async with NetworkFileSystem.ephemeral() as nfs:
159
- assert await nfs.listdir() == []
159
+ ```python notest
160
+ async with modal.NetworkFileSystem.ephemeral() as nfs:
161
+ assert await nfs.listdir("/") == []
160
162
  ```
161
163
  """
162
164
  if client is None:
@@ -184,7 +186,7 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
184
186
  In contrast to `modal.NetworkFileSystem.from_name`, this is an eager method
185
187
  that will hydrate the local object with metadata from Modal servers.
186
188
 
187
- ```python
189
+ ```python notest
188
190
  nfs = modal.NetworkFileSystem.lookup("my-nfs")
189
191
  print(nfs.listdir("/"))
190
192
  ```
modal/queue.py CHANGED
@@ -126,7 +126,9 @@ class _Queue(_Object, type_prefix="qu"):
126
126
 
127
127
  with Queue.ephemeral() as q:
128
128
  q.put(123)
129
+ ```
129
130
 
131
+ ```python notest
130
132
  async with Queue.ephemeral() as q:
131
133
  await q.put.aio(123)
132
134
  ```
modal/volume.py CHANGED
@@ -200,11 +200,14 @@ class _Volume(_Object, type_prefix="vo"):
200
200
 
201
201
  Usage:
202
202
  ```python
203
- with Volume.ephemeral() as vol:
204
- assert vol.listdir() == []
203
+ import modal
204
+ with modal.Volume.ephemeral() as vol:
205
+ assert vol.listdir("/") == []
206
+ ```
205
207
 
206
- async with Volume.ephemeral() as vol:
207
- assert await vol.listdir() == []
208
+ ```python notest
209
+ async with modal.Volume.ephemeral() as vol:
210
+ assert await vol.listdir("/") == []
208
211
  ```
209
212
  """
210
213
  if client is None:
@@ -234,7 +237,7 @@ class _Volume(_Object, type_prefix="vo"):
234
237
  In contrast to `modal.Volume.from_name`, this is an eager method
235
238
  that will hydrate the local object with metadata from Modal servers.
236
239
 
237
- ```python
240
+ ```python notest
238
241
  vol = modal.Volume.lookup("my-volume")
239
242
  print(vol.listdir("/"))
240
243
  ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: modal
3
- Version: 0.67.1
3
+ Version: 0.67.6
4
4
  Summary: Python client library for Modal
5
5
  Author: Modal Labs
6
6
  Author-email: support@modal.com
@@ -12,37 +12,37 @@ modal/_resolver.py,sha256=s2YSLnpZbwUQHLs2ahltvEVDMLRYyIagdTcWr2-qkeA,6760
12
12
  modal/_resources.py,sha256=zTCcXvmG1erERnTvUcN0r4IjjwVEs4N8fs7OnkpvbJQ,1740
13
13
  modal/_serialization.py,sha256=b1X44hDP7WDTX3iz3HrPyFJJz51a69luq5Tyhnm1fK0,18762
14
14
  modal/_traceback.py,sha256=1yNp1Dqq4qRIQp8idDp5PEqjwH4eA8MNI0FhFkCOFgo,4408
15
- modal/_tunnel.py,sha256=SVmQxGbV7dcLwyY9eB2PIWmXw8QQmcKg2ppTcRQgZrU,6283
15
+ modal/_tunnel.py,sha256=BC9Fbvr9xvE3UMp5t8rn4ek-jXu2uwU778cKPMJfTtM,6290
16
16
  modal/_tunnel.pyi,sha256=SA_Q0UGB-D9skFjv6CqlTnCEWU67a2xJxfwVdXtar3Y,1259
17
17
  modal/_watcher.py,sha256=STlDe73R7IS33a_GMW2HnDc3hCDKLdsBfMxRpVh-flA,3581
18
- modal/app.py,sha256=IBOZQBq6T3l-uzNCM4eFWLkWfcc7r3kQExbKpcN4qgA,45486
19
- modal/app.pyi,sha256=sX2BXX_178lp8O_GvwZqsxDdxQi1j3DjNfthMvlMlJU,25273
18
+ modal/app.py,sha256=J2vcc3MDUnawxgEe-WXvfISPGLLcdCPlCqnYk9TeP2I,46114
19
+ modal/app.pyi,sha256=AWJVT92FaRN7DvSA5tD5w3_E-_PJeEPXTAf8rcwxR60,25312
20
20
  modal/call_graph.py,sha256=l-Wi6vM8aosCdHTWegcCyGeVJGFdZ_fzlCmbRVPBXFI,2593
21
21
  modal/client.py,sha256=4SpWb4n0nolITR36kADZl1tYLOg6avukmzZU56UQjCo,16385
22
- modal/client.pyi,sha256=suptGj0uNTcyh19J_hlCmXDW6gj9ojLAeoyqU-6Vb8U,7370
22
+ modal/client.pyi,sha256=hJ4icHlSyz3LcVDSF0myB0Zi5G2mVoMS4Vr4uqiQKJY,7370
23
23
  modal/cloud_bucket_mount.py,sha256=eWQhCtMIczpokjfTZEgNBCGO_s5ft46PqTSLfKBykq4,5748
24
24
  modal/cloud_bucket_mount.pyi,sha256=tTF7M4FR9bTA30cFkz8qq3ZTlFL19NHU_36e_5GgAGA,1424
25
- modal/cls.py,sha256=W3fGE7wdShFwCeWS1oT0LX2_SHBCXy04DgfVt4ggXZA,24692
25
+ modal/cls.py,sha256=v3Uad6nTpHNwkAZ04KlQY3jZGgntKt5Onz6DusFRtg8,24706
26
26
  modal/cls.pyi,sha256=eVQ3QQC2XwGe2rvYFvjQ57d8ToBXbpI6M-9_YyVQkbo,8214
27
27
  modal/config.py,sha256=bij_YRIynxQtwDmS73BNMR9YZf6CHcqbFYdfRqDrWV0,10929
28
28
  modal/container_process.py,sha256=c_jBPtyPeSxbIcbLfs_FzTrt-1eErtRSnsfxkDozFoY,5589
29
29
  modal/container_process.pyi,sha256=k2kClwaSzz11eci1pzFZgCm-ptXapHAyHTOENorlazA,2594
30
- modal/dict.py,sha256=axbUKiXhgOVvE1IrNMK3uHg3rp3N0Uji5elQNijnhH4,12571
30
+ modal/dict.py,sha256=ncy0q21IU2PqZa4CpFfPfNIMWpOLwKvdU40xZhxCemc,12608
31
31
  modal/dict.pyi,sha256=4Rh5HNzXk99hotPUzoj1kYOHNlEgndZFfA-hlmBtBIc,7223
32
32
  modal/environments.py,sha256=KwKdrWfSnz2XF5eYXZyd0kdac1x1PVw2uxPYsGy8cys,6517
33
33
  modal/environments.pyi,sha256=oScvFAclF55-tL9UioLIL_SPBwgy_9O-BBvJ-PLbRgY,3542
34
34
  modal/exception.py,sha256=K-czk1oK8wFvK8snWrytXSByo2WNb9SJAlgBVPGWZBs,6417
35
35
  modal/experimental.py,sha256=jFuNbwrNHos47viMB9q-cHJSvf2RDxDdoEcss9plaZE,2302
36
- modal/functions.py,sha256=Z7nTQ51gMyjtSNQNEj55nEhoGWm2scHAFaShfOdPVMM,66939
37
- modal/functions.pyi,sha256=5i5CTK6Eic-80o8NAPXHPujg_qpLj9d95eN71lpYSxE,24375
36
+ modal/functions.py,sha256=Gfr-IsAeH0uIHCSfVYNWSzjnnrN33LPY3Ui4S6c6Pvo,66953
37
+ modal/functions.pyi,sha256=1pFjUQBZx__UGaqJadYoMycQdlVb4jTex307VREzpQA,24375
38
38
  modal/gpu.py,sha256=r4rL6uH3UJIQthzYvfWauXNyh01WqCPtKZCmmSX1fd4,6881
39
- modal/image.py,sha256=f4OB4gfyaSz3mQjumzEMeZT4Uq0SzsGBMN5NkPQQSec,79550
39
+ modal/image.py,sha256=DoubwAD9ebYNIEO3GJWbtAaJl_ZRW4K0tpIJdS9CZAg,79551
40
40
  modal/image.pyi,sha256=3rfae_E0KuNHqdi5j33nHXp_7P3tTkt7QKH5cXYczUc,24672
41
- modal/io_streams.py,sha256=XUsNsxRzDrhkjyb2Hx0hugCoOEz266SHQF8wP-VgsfY,14582
41
+ modal/io_streams.py,sha256=AAY-qcISGCIMp-F6SwX6vhZWJv7Sg_KudDBW0rMfc-E,14637
42
42
  modal/io_streams.pyi,sha256=WJmSI1WvZITUNBO7mnIuJgYdSKdbLaHk10V4GbttAVw,4452
43
43
  modal/mount.py,sha256=QZ4nabpbNU9tjLIPCq86rlHor9CXzADMkhJWBYfKKgg,27750
44
44
  modal/mount.pyi,sha256=nywUmeUELLY2OEnAc1NNBHmSxuEylTWBzkh6nuXkkuc,9965
45
- modal/network_file_system.py,sha256=P_LsILecyda1SRHU76Hk4Lq3M1HSx9shFJbaLThzw0U,14071
45
+ modal/network_file_system.py,sha256=xCQDM8aajiGydGVm8BaJo0yTgsceHNMmAfG5D3L3d_I,14133
46
46
  modal/network_file_system.pyi,sha256=XLyUnDx55ExbJcF_xlKxRax_r06XTvSsQh-a-_EyCOU,7239
47
47
  modal/object.py,sha256=Qgee_lQJY_583YsGIVrSNuDF_gJA_qmTAeVTVI1tf-g,9637
48
48
  modal/object.pyi,sha256=uGGD5A2B_mj8jxLfFiHama5wzCcBS_GNvPSKsIfsCO0,8518
@@ -54,7 +54,7 @@ modal/partial_function.pyi,sha256=BqKN7It5QLxS2yhhhDX0RgI8EyNMPBD6Duk21kN_fvA,89
54
54
  modal/proxy.py,sha256=ZrOsuQP7dSZFq1OrIxalNnt0Zvsnp1h86Th679sSL40,1417
55
55
  modal/proxy.pyi,sha256=UvygdOYneLTuoDY6hVaMNCyZ947Tmx93IdLjErUqkvM,368
56
56
  modal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
- modal/queue.py,sha256=qCBE-V2QRl_taSDUR5bDJL91J0A8xwbTxfEU3taA4Es,18338
57
+ modal/queue.py,sha256=WdiU0eiBtQx3tOk-QojyScieHZRkIqaXWQdJwCMlDCc,18375
58
58
  modal/queue.pyi,sha256=t5yhDqXRtexy7AVraCexPyT6Xo3QA_H5OxVe_JLzTps,9999
59
59
  modal/retries.py,sha256=z4dYXdksUcjkefM3vGLkhCQ_m_TUPLJgC4uSYDzWSOU,3750
60
60
  modal/runner.py,sha256=Yx8SdnxGzHIlqMxhIQiBukF12yj7wh-c6jWwy8w76u8,24081
@@ -71,17 +71,17 @@ modal/serving.pyi,sha256=0KWUH5rdYnihSv1XB8bK9GokzpfzrCq8Sf6nYlUvQI8,1689
71
71
  modal/stream_type.py,sha256=A6320qoAAWhEfwOCZfGtymQTu5AfLfJXXgARqooTPvY,417
72
72
  modal/token_flow.py,sha256=lsVpJACut76AeJLw44vJKMSlpcqp8wcvxdUOoX6CIOc,6754
73
73
  modal/token_flow.pyi,sha256=qEYP7grgqSA440w7kBREU9Ezeo_NxCT67OciIPgDzcc,1958
74
- modal/volume.py,sha256=5IdcerxXjP9MpAZm9QXPTWRDYZD5UJSFebWGglCha8k,29301
74
+ modal/volume.py,sha256=uq1hyHXRdCG10rbIDWq6B9mveeJlOE5mXWKUaynhN4o,29384
75
75
  modal/volume.pyi,sha256=3lB6wiC75u3o44cwJVqDsmvR4wsP2JXSxJrVXi9KrK4,11127
76
76
  modal/_runtime/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
77
- modal/_runtime/asgi.py,sha256=WoAwIiGKpk089MOca3_iA73h36v0uBuoPx0-87ajIDY,19843
77
+ modal/_runtime/asgi.py,sha256=69X60gSg6AnH8dZlMC1C5eWkC0nypewbHbzAcy9-_gg,21523
78
78
  modal/_runtime/container_io_manager.py,sha256=qFxBdmOh6Vz_Jc9HW27yM-Wt6rOvhMi1yHUW1CzCneg,44146
79
79
  modal/_runtime/execution_context.py,sha256=cXEVY4wEK-oZJVJptyj1ZplZvVQ1HDzFsyHxhaY4o8M,2718
80
80
  modal/_runtime/telemetry.py,sha256=3NbrfwYH6mvDckzdTppymmda2lQKX2oHGc2JwdFZdUc,5191
81
81
  modal/_runtime/user_code_imports.py,sha256=2COhqA77zwbP__-DWiDHEScHM-Go3CmI-AlKvT_oBOU,14545
82
82
  modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
83
83
  modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
84
- modal/_utils/async_utils.py,sha256=3H4pBC4DW6rCA6hgRux6FMxGqPgHM-G-BTs7KXZiWz4,23958
84
+ modal/_utils/async_utils.py,sha256=LInaa9xOl256MwXPtFlgmgk9iS2OEJGF15BCyi_O0hE,24994
85
85
  modal/_utils/blob_utils.py,sha256=pAY22w0oVc6ujGfI7La7HPUMOf42FehIapuhSDeeqEs,15835
86
86
  modal/_utils/function_utils.py,sha256=28mxgg_-7JF1DDiNnp3iNVsFQkOzMFjORsetdvZZTHU,24475
87
87
  modal/_utils/grpc_testing.py,sha256=LOzWygTdHINzV-o_Ajbl7sOFbUQFoonP0iKpsJjA_nc,8301
@@ -159,10 +159,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
159
159
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
160
160
  modal_version/__init__.py,sha256=3IY-AWLH55r35_mQXIaut0jrJvoPuf1NZJBQQfSbPuo,470
161
161
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
162
- modal_version/_version_generated.py,sha256=rovVazB4vqHr5HAyfiVAZJmm1D6GrZvk39X1IbGTkRg,148
163
- modal-0.67.1.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
164
- modal-0.67.1.dist-info/METADATA,sha256=65AuiyGXtdF6hTgAgaLzS3cSAe6mRofbhZo_ngXIb-c,2328
165
- modal-0.67.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
166
- modal-0.67.1.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
167
- modal-0.67.1.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
168
- modal-0.67.1.dist-info/RECORD,,
162
+ modal_version/_version_generated.py,sha256=w1TBMXGTDLGjMZ9q-CYFNmAgzYtrFjHKc0nUcuwOGEg,148
163
+ modal-0.67.6.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
164
+ modal-0.67.6.dist-info/METADATA,sha256=gEvosk6PchWUcuXAFRrWJcWX8J3vWX6bd9G_55uJ88o,2328
165
+ modal-0.67.6.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
166
+ modal-0.67.6.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
167
+ modal-0.67.6.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
168
+ modal-0.67.6.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2024
2
2
 
3
3
  # Note: Reset this value to -1 whenever you make a minor `0.X` release of the client.
4
- build_number = 1 # git: bd320d8
4
+ build_number = 6 # git: d4f8c25
File without changes