modal 0.68.15__py3-none-any.whl → 0.68.16__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 +4 -0
- modal/_runtime/user_code_imports.py +13 -18
- modal/_utils/blob_utils.py +9 -87
- modal/_utils/bytes_io_segment_payload.py +97 -0
- modal/_utils/http_utils.py +19 -10
- modal/client.pyi +2 -2
- {modal-0.68.15.dist-info → modal-0.68.16.dist-info}/METADATA +1 -1
- {modal-0.68.15.dist-info → modal-0.68.16.dist-info}/RECORD +13 -12
- modal_version/_version_generated.py +1 -1
- {modal-0.68.15.dist-info → modal-0.68.16.dist-info}/LICENSE +0 -0
- {modal-0.68.15.dist-info → modal-0.68.16.dist-info}/WHEEL +0 -0
- {modal-0.68.15.dist-info → modal-0.68.16.dist-info}/entry_points.txt +0 -0
- {modal-0.68.15.dist-info → modal-0.68.16.dist-info}/top_level.txt +0 -0
modal/_runtime/asgi.py
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
# Copyright Modal Labs 2022
|
2
|
+
|
3
|
+
# Note: this module isn't imported unless it's needed.
|
4
|
+
# This is because aiohttp is a pretty big dependency that adds significant latency when imported
|
5
|
+
|
2
6
|
import asyncio
|
3
7
|
from collections.abc import AsyncGenerator
|
4
8
|
from typing import Any, Callable, NoReturn, Optional, cast
|
@@ -9,15 +9,6 @@ import modal._runtime.container_io_manager
|
|
9
9
|
import modal.cls
|
10
10
|
import modal.object
|
11
11
|
from modal import Function
|
12
|
-
from modal._runtime.asgi import (
|
13
|
-
LifespanManager,
|
14
|
-
asgi_app_wrapper,
|
15
|
-
get_ip_address,
|
16
|
-
wait_for_web_server,
|
17
|
-
web_server_proxy,
|
18
|
-
webhook_asgi_app,
|
19
|
-
wsgi_app_wrapper,
|
20
|
-
)
|
21
12
|
from modal._utils.async_utils import synchronizer
|
22
13
|
from modal._utils.function_utils import LocalFunctionError, is_async as get_is_async, is_global_object
|
23
14
|
from modal.exception import ExecutionError, InvalidError
|
@@ -28,6 +19,7 @@ from modal_proto import api_pb2
|
|
28
19
|
if typing.TYPE_CHECKING:
|
29
20
|
import modal.app
|
30
21
|
import modal.partial_function
|
22
|
+
from modal._runtime.asgi import LifespanManager
|
31
23
|
|
32
24
|
|
33
25
|
@dataclass
|
@@ -36,7 +28,7 @@ class FinalizedFunction:
|
|
36
28
|
is_async: bool
|
37
29
|
is_generator: bool
|
38
30
|
data_format: int # api_pb2.DataFormat
|
39
|
-
lifespan_manager: Optional[LifespanManager] = None
|
31
|
+
lifespan_manager: Optional["LifespanManager"] = None
|
40
32
|
|
41
33
|
|
42
34
|
class Service(metaclass=ABCMeta):
|
@@ -63,19 +55,22 @@ def construct_webhook_callable(
|
|
63
55
|
webhook_config: api_pb2.WebhookConfig,
|
64
56
|
container_io_manager: "modal._runtime.container_io_manager.ContainerIOManager",
|
65
57
|
):
|
58
|
+
# Note: aiohttp is a significant dependency of the `asgi` module, so we import it locally
|
59
|
+
from modal._runtime import asgi
|
60
|
+
|
66
61
|
# For webhooks, the user function is used to construct an asgi app:
|
67
62
|
if webhook_config.type == api_pb2.WEBHOOK_TYPE_ASGI_APP:
|
68
63
|
# Function returns an asgi_app, which we can use as a callable.
|
69
|
-
return asgi_app_wrapper(user_defined_callable(), container_io_manager)
|
64
|
+
return asgi.asgi_app_wrapper(user_defined_callable(), container_io_manager)
|
70
65
|
|
71
66
|
elif webhook_config.type == api_pb2.WEBHOOK_TYPE_WSGI_APP:
|
72
|
-
# Function returns an wsgi_app, which we can use as a callable
|
73
|
-
return wsgi_app_wrapper(user_defined_callable(), container_io_manager)
|
67
|
+
# Function returns an wsgi_app, which we can use as a callable
|
68
|
+
return asgi.wsgi_app_wrapper(user_defined_callable(), container_io_manager)
|
74
69
|
|
75
70
|
elif webhook_config.type == api_pb2.WEBHOOK_TYPE_FUNCTION:
|
76
71
|
# Function is a webhook without an ASGI app. Create one for it.
|
77
|
-
return asgi_app_wrapper(
|
78
|
-
webhook_asgi_app(user_defined_callable, webhook_config.method, webhook_config.web_endpoint_docs),
|
72
|
+
return asgi.asgi_app_wrapper(
|
73
|
+
asgi.webhook_asgi_app(user_defined_callable, webhook_config.method, webhook_config.web_endpoint_docs),
|
79
74
|
container_io_manager,
|
80
75
|
)
|
81
76
|
|
@@ -86,11 +81,11 @@ def construct_webhook_callable(
|
|
86
81
|
# We intentionally try to connect to the external interface instead of the loopback
|
87
82
|
# interface here so users are forced to expose the server. This allows us to potentially
|
88
83
|
# change the implementation to use an external bridge in the future.
|
89
|
-
host = get_ip_address(b"eth0")
|
84
|
+
host = asgi.get_ip_address(b"eth0")
|
90
85
|
port = webhook_config.web_server_port
|
91
86
|
startup_timeout = webhook_config.web_server_startup_timeout
|
92
|
-
wait_for_web_server(host, port, timeout=startup_timeout)
|
93
|
-
return asgi_app_wrapper(web_server_proxy(host, port), container_io_manager)
|
87
|
+
asgi.wait_for_web_server(host, port, timeout=startup_timeout)
|
88
|
+
return asgi.asgi_app_wrapper(asgi.web_server_proxy(host, port), container_io_manager)
|
94
89
|
else:
|
95
90
|
raise InvalidError(f"Unrecognized web endpoint type {webhook_config.type}")
|
96
91
|
|
modal/_utils/blob_utils.py
CHANGED
@@ -9,12 +9,9 @@ import time
|
|
9
9
|
from collections.abc import AsyncIterator
|
10
10
|
from contextlib import AbstractContextManager, contextmanager
|
11
11
|
from pathlib import Path, PurePosixPath
|
12
|
-
from typing import Any, BinaryIO, Callable, Optional, Union
|
12
|
+
from typing import TYPE_CHECKING, Any, BinaryIO, Callable, Optional, Union
|
13
13
|
from urllib.parse import urlparse
|
14
14
|
|
15
|
-
from aiohttp import BytesIOPayload
|
16
|
-
from aiohttp.abc import AbstractStreamWriter
|
17
|
-
|
18
15
|
from modal_proto import api_pb2
|
19
16
|
from modal_proto.modal_api_grpc import ModalClientModal
|
20
17
|
|
@@ -25,6 +22,9 @@ from .hash_utils import UploadHashes, get_upload_hashes
|
|
25
22
|
from .http_utils import ClientSessionRegistry
|
26
23
|
from .logger import logger
|
27
24
|
|
25
|
+
if TYPE_CHECKING:
|
26
|
+
from .bytes_io_segment_payload import BytesIOSegmentPayload
|
27
|
+
|
28
28
|
# Max size for function inputs and outputs.
|
29
29
|
MAX_OBJECT_SIZE_BYTES = 2 * 1024 * 1024 # 2 MiB
|
30
30
|
|
@@ -44,92 +44,10 @@ DEFAULT_SEGMENT_CHUNK_SIZE = 2**24
|
|
44
44
|
MULTIPART_UPLOAD_THRESHOLD = 1024**3
|
45
45
|
|
46
46
|
|
47
|
-
class BytesIOSegmentPayload(BytesIOPayload):
|
48
|
-
"""Modified bytes payload for concurrent sends of chunks from the same file.
|
49
|
-
|
50
|
-
Adds:
|
51
|
-
* read limit using remaining_bytes, in order to split files across streams
|
52
|
-
* larger read chunk (to prevent excessive read contention between parts)
|
53
|
-
* calculates an md5 for the segment
|
54
|
-
|
55
|
-
Feels like this should be in some standard lib...
|
56
|
-
"""
|
57
|
-
|
58
|
-
def __init__(
|
59
|
-
self,
|
60
|
-
bytes_io: BinaryIO, # should *not* be shared as IO position modification is not locked
|
61
|
-
segment_start: int,
|
62
|
-
segment_length: int,
|
63
|
-
chunk_size: int = DEFAULT_SEGMENT_CHUNK_SIZE,
|
64
|
-
progress_report_cb: Optional[Callable] = None,
|
65
|
-
):
|
66
|
-
# not thread safe constructor!
|
67
|
-
super().__init__(bytes_io)
|
68
|
-
self.initial_seek_pos = bytes_io.tell()
|
69
|
-
self.segment_start = segment_start
|
70
|
-
self.segment_length = segment_length
|
71
|
-
# seek to start of file segment we are interested in, in order to make .size() evaluate correctly
|
72
|
-
self._value.seek(self.initial_seek_pos + segment_start)
|
73
|
-
assert self.segment_length <= super().size
|
74
|
-
self.chunk_size = chunk_size
|
75
|
-
self.progress_report_cb = progress_report_cb or (lambda *_, **__: None)
|
76
|
-
self.reset_state()
|
77
|
-
|
78
|
-
def reset_state(self):
|
79
|
-
self._md5_checksum = hashlib.md5()
|
80
|
-
self.num_bytes_read = 0
|
81
|
-
self._value.seek(self.initial_seek_pos)
|
82
|
-
|
83
|
-
@contextmanager
|
84
|
-
def reset_on_error(self):
|
85
|
-
try:
|
86
|
-
yield
|
87
|
-
except Exception as exc:
|
88
|
-
try:
|
89
|
-
self.progress_report_cb(reset=True)
|
90
|
-
except Exception as cb_exc:
|
91
|
-
raise cb_exc from exc
|
92
|
-
raise exc
|
93
|
-
finally:
|
94
|
-
self.reset_state()
|
95
|
-
|
96
|
-
@property
|
97
|
-
def size(self) -> int:
|
98
|
-
return self.segment_length
|
99
|
-
|
100
|
-
def md5_checksum(self):
|
101
|
-
return self._md5_checksum
|
102
|
-
|
103
|
-
async def write(self, writer: AbstractStreamWriter):
|
104
|
-
loop = asyncio.get_event_loop()
|
105
|
-
|
106
|
-
async def safe_read():
|
107
|
-
read_start = self.initial_seek_pos + self.segment_start + self.num_bytes_read
|
108
|
-
self._value.seek(read_start)
|
109
|
-
num_bytes = min(self.chunk_size, self.remaining_bytes())
|
110
|
-
chunk = await loop.run_in_executor(None, self._value.read, num_bytes)
|
111
|
-
|
112
|
-
await loop.run_in_executor(None, self._md5_checksum.update, chunk)
|
113
|
-
self.num_bytes_read += len(chunk)
|
114
|
-
return chunk
|
115
|
-
|
116
|
-
chunk = await safe_read()
|
117
|
-
while chunk and self.remaining_bytes() > 0:
|
118
|
-
await writer.write(chunk)
|
119
|
-
self.progress_report_cb(advance=len(chunk))
|
120
|
-
chunk = await safe_read()
|
121
|
-
if chunk:
|
122
|
-
await writer.write(chunk)
|
123
|
-
self.progress_report_cb(advance=len(chunk))
|
124
|
-
|
125
|
-
def remaining_bytes(self):
|
126
|
-
return self.segment_length - self.num_bytes_read
|
127
|
-
|
128
|
-
|
129
47
|
@retry(n_attempts=5, base_delay=0.5, timeout=None)
|
130
48
|
async def _upload_to_s3_url(
|
131
49
|
upload_url,
|
132
|
-
payload: BytesIOSegmentPayload,
|
50
|
+
payload: "BytesIOSegmentPayload",
|
133
51
|
content_md5_b64: Optional[str] = None,
|
134
52
|
content_type: Optional[str] = "application/octet-stream", # set to None to force omission of ContentType header
|
135
53
|
) -> str:
|
@@ -185,6 +103,8 @@ async def perform_multipart_upload(
|
|
185
103
|
upload_chunk_size: int = DEFAULT_SEGMENT_CHUNK_SIZE,
|
186
104
|
progress_report_cb: Optional[Callable] = None,
|
187
105
|
) -> None:
|
106
|
+
from .bytes_io_segment_payload import BytesIOSegmentPayload
|
107
|
+
|
188
108
|
upload_coros = []
|
189
109
|
file_offset = 0
|
190
110
|
num_bytes_left = content_length
|
@@ -278,6 +198,8 @@ async def _blob_upload(
|
|
278
198
|
progress_report_cb=progress_report_cb,
|
279
199
|
)
|
280
200
|
else:
|
201
|
+
from .bytes_io_segment_payload import BytesIOSegmentPayload
|
202
|
+
|
281
203
|
payload = BytesIOSegmentPayload(
|
282
204
|
data, segment_start=0, segment_length=content_length, progress_report_cb=progress_report_cb
|
283
205
|
)
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# Copyright Modal Labs 2024
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
import hashlib
|
5
|
+
from contextlib import contextmanager
|
6
|
+
from typing import BinaryIO, Callable, Optional
|
7
|
+
|
8
|
+
# Note: this module needs to import aiohttp in global scope
|
9
|
+
# This takes about 50ms and isn't needed in many cases for Modal execution
|
10
|
+
# To avoid this, we import it in local scope when needed (blob_utils.py)
|
11
|
+
from aiohttp import BytesIOPayload
|
12
|
+
from aiohttp.abc import AbstractStreamWriter
|
13
|
+
|
14
|
+
# read ~16MiB chunks by default
|
15
|
+
DEFAULT_SEGMENT_CHUNK_SIZE = 2**24
|
16
|
+
|
17
|
+
|
18
|
+
class BytesIOSegmentPayload(BytesIOPayload):
|
19
|
+
"""Modified bytes payload for concurrent sends of chunks from the same file.
|
20
|
+
|
21
|
+
Adds:
|
22
|
+
* read limit using remaining_bytes, in order to split files across streams
|
23
|
+
* larger read chunk (to prevent excessive read contention between parts)
|
24
|
+
* calculates an md5 for the segment
|
25
|
+
|
26
|
+
Feels like this should be in some standard lib...
|
27
|
+
"""
|
28
|
+
|
29
|
+
def __init__(
|
30
|
+
self,
|
31
|
+
bytes_io: BinaryIO, # should *not* be shared as IO position modification is not locked
|
32
|
+
segment_start: int,
|
33
|
+
segment_length: int,
|
34
|
+
chunk_size: int = DEFAULT_SEGMENT_CHUNK_SIZE,
|
35
|
+
progress_report_cb: Optional[Callable] = None,
|
36
|
+
):
|
37
|
+
# not thread safe constructor!
|
38
|
+
super().__init__(bytes_io)
|
39
|
+
self.initial_seek_pos = bytes_io.tell()
|
40
|
+
self.segment_start = segment_start
|
41
|
+
self.segment_length = segment_length
|
42
|
+
# seek to start of file segment we are interested in, in order to make .size() evaluate correctly
|
43
|
+
self._value.seek(self.initial_seek_pos + segment_start)
|
44
|
+
assert self.segment_length <= super().size
|
45
|
+
self.chunk_size = chunk_size
|
46
|
+
self.progress_report_cb = progress_report_cb or (lambda *_, **__: None)
|
47
|
+
self.reset_state()
|
48
|
+
|
49
|
+
def reset_state(self):
|
50
|
+
self._md5_checksum = hashlib.md5()
|
51
|
+
self.num_bytes_read = 0
|
52
|
+
self._value.seek(self.initial_seek_pos)
|
53
|
+
|
54
|
+
@contextmanager
|
55
|
+
def reset_on_error(self):
|
56
|
+
try:
|
57
|
+
yield
|
58
|
+
except Exception as exc:
|
59
|
+
try:
|
60
|
+
self.progress_report_cb(reset=True)
|
61
|
+
except Exception as cb_exc:
|
62
|
+
raise cb_exc from exc
|
63
|
+
raise exc
|
64
|
+
finally:
|
65
|
+
self.reset_state()
|
66
|
+
|
67
|
+
@property
|
68
|
+
def size(self) -> int:
|
69
|
+
return self.segment_length
|
70
|
+
|
71
|
+
def md5_checksum(self):
|
72
|
+
return self._md5_checksum
|
73
|
+
|
74
|
+
async def write(self, writer: "AbstractStreamWriter"):
|
75
|
+
loop = asyncio.get_event_loop()
|
76
|
+
|
77
|
+
async def safe_read():
|
78
|
+
read_start = self.initial_seek_pos + self.segment_start + self.num_bytes_read
|
79
|
+
self._value.seek(read_start)
|
80
|
+
num_bytes = min(self.chunk_size, self.remaining_bytes())
|
81
|
+
chunk = await loop.run_in_executor(None, self._value.read, num_bytes)
|
82
|
+
|
83
|
+
await loop.run_in_executor(None, self._md5_checksum.update, chunk)
|
84
|
+
self.num_bytes_read += len(chunk)
|
85
|
+
return chunk
|
86
|
+
|
87
|
+
chunk = await safe_read()
|
88
|
+
while chunk and self.remaining_bytes() > 0:
|
89
|
+
await writer.write(chunk)
|
90
|
+
self.progress_report_cb(advance=len(chunk))
|
91
|
+
chunk = await safe_read()
|
92
|
+
if chunk:
|
93
|
+
await writer.write(chunk)
|
94
|
+
self.progress_report_cb(advance=len(chunk))
|
95
|
+
|
96
|
+
def remaining_bytes(self):
|
97
|
+
return self.segment_length - self.num_bytes_read
|
modal/_utils/http_utils.py
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
# Copyright Modal Labs 2022
|
2
2
|
import contextlib
|
3
|
-
import
|
4
|
-
import ssl
|
5
|
-
from typing import Optional
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
# Note: importing aiohttp seems to take about 100ms, and it's not really necessarily,
|
6
|
+
# unless we need to work with blobs. So that's why we import it lazily instead.
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from aiohttp import ClientSession
|
10
|
+
from aiohttp.web import Application
|
11
11
|
|
12
12
|
from .async_utils import on_shutdown
|
13
13
|
|
14
14
|
|
15
|
-
def _http_client_with_tls(timeout: Optional[float]) -> ClientSession:
|
15
|
+
def _http_client_with_tls(timeout: Optional[float]) -> "ClientSession":
|
16
16
|
"""Create a new HTTP client session with standard, bundled TLS certificates.
|
17
17
|
|
18
18
|
This is necessary to prevent client issues on some system where Python does
|
@@ -22,13 +22,18 @@ def _http_client_with_tls(timeout: Optional[float]) -> ClientSession:
|
|
22
22
|
Specifically: the error "unable to get local issuer certificate" when making
|
23
23
|
an aiohttp request.
|
24
24
|
"""
|
25
|
+
import ssl
|
26
|
+
|
27
|
+
import certifi
|
28
|
+
from aiohttp import ClientSession, ClientTimeout, TCPConnector
|
29
|
+
|
25
30
|
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
26
31
|
connector = TCPConnector(ssl=ssl_context)
|
27
32
|
return ClientSession(connector=connector, timeout=ClientTimeout(total=timeout))
|
28
33
|
|
29
34
|
|
30
35
|
class ClientSessionRegistry:
|
31
|
-
_client_session: ClientSession
|
36
|
+
_client_session: "ClientSession"
|
32
37
|
_client_session_active: bool = False
|
33
38
|
|
34
39
|
@staticmethod
|
@@ -47,9 +52,13 @@ class ClientSessionRegistry:
|
|
47
52
|
|
48
53
|
|
49
54
|
@contextlib.asynccontextmanager
|
50
|
-
async def run_temporary_http_server(app: Application):
|
55
|
+
async def run_temporary_http_server(app: "Application"):
|
51
56
|
# Allocates a random port, runs a server in a context manager
|
52
57
|
# This is used in various tests
|
58
|
+
import socket
|
59
|
+
|
60
|
+
from aiohttp.web_runner import AppRunner, SockSite
|
61
|
+
|
53
62
|
sock = socket.socket()
|
54
63
|
sock.bind(("", 0))
|
55
64
|
port = sock.getsockname()[1]
|
modal/client.pyi
CHANGED
@@ -26,7 +26,7 @@ class _Client:
|
|
26
26
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
27
27
|
|
28
28
|
def __init__(
|
29
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.
|
29
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.16"
|
30
30
|
): ...
|
31
31
|
def is_closed(self) -> bool: ...
|
32
32
|
@property
|
@@ -81,7 +81,7 @@ class Client:
|
|
81
81
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
82
82
|
|
83
83
|
def __init__(
|
84
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.
|
84
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.16"
|
85
85
|
): ...
|
86
86
|
def is_closed(self) -> bool: ...
|
87
87
|
@property
|
@@ -19,7 +19,7 @@ modal/app.py,sha256=EJ7FUN6rWnSwLJoYJh8nmKg_t-8hdN8_rt0OrkP7JvQ,46084
|
|
19
19
|
modal/app.pyi,sha256=BE5SlR5tRECuc6-e2lUuOknDdov3zxgZ4N0AsLb5ZVQ,25270
|
20
20
|
modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
|
21
21
|
modal/client.py,sha256=nyPjfromWBeOyurexpFP2QLQNk822RPggMCLyX9j1jA,15247
|
22
|
-
modal/client.pyi,sha256=
|
22
|
+
modal/client.pyi,sha256=457zwWltZmkWx6kL2KgLC69XMQYUSrLXVhYsT-Rcit8,7280
|
23
23
|
modal/cloud_bucket_mount.py,sha256=G7T7jWLD0QkmrfKR75mSTwdUZ2xNfj7pkVqb4ipmxmI,5735
|
24
24
|
modal/cloud_bucket_mount.pyi,sha256=CEi7vrH3kDUF4LAy4qP6tfImy2UJuFRcRbsgRNM1wo8,1403
|
25
25
|
modal/cls.py,sha256=ONnrfZ2vPcaY2JuKypPiBA9eTiyg8Qfg-Ull40nn9zs,30956
|
@@ -76,20 +76,21 @@ modal/token_flow.pyi,sha256=gOYtYujrWt_JFZeiI8EmfahXPx5GCR5Na-VaPQcWgEY,1937
|
|
76
76
|
modal/volume.py,sha256=PGzbninvRU-IhSwJgM2jZKzD8llRhZhadsOxZ-YNwaM,29316
|
77
77
|
modal/volume.pyi,sha256=St0mDiaojfep6Bs4sBbkRJmeacYHF6lh6FKOWGmheHA,11182
|
78
78
|
modal/_runtime/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
79
|
-
modal/_runtime/asgi.py,sha256=
|
79
|
+
modal/_runtime/asgi.py,sha256=H68KAN8bz8Zp7EcRl2c_ite1Y3kP1MHvjQAf-uUpCx8,21691
|
80
80
|
modal/_runtime/container_io_manager.py,sha256=ctgyNFiHjq1brCrabXmlurkAXjnrCeWPRvTVa735vRw,44215
|
81
81
|
modal/_runtime/execution_context.py,sha256=E6ofm6j1POXGPxS841X3V7JU6NheVb8OkQc7JpLq4Kg,2712
|
82
82
|
modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5163
|
83
|
-
modal/_runtime/user_code_imports.py,sha256=
|
83
|
+
modal/_runtime/user_code_imports.py,sha256=n4CQOojzSdf0jwSKSy6MEnVX3IWl3t3Dq54-x9VS2Ds,14663
|
84
84
|
modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
|
85
85
|
modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
|
86
86
|
modal/_utils/async_utils.py,sha256=9ubwMkwiDB4gzOYG2jL9j7Fs-5dxHjcifZe3r7JRg-k,25091
|
87
|
-
modal/_utils/blob_utils.py,sha256=
|
87
|
+
modal/_utils/blob_utils.py,sha256=N66LtZI8PpCkZ7maA7GLW5CAmYUoNJdG-GjaAUR4_NQ,14509
|
88
|
+
modal/_utils/bytes_io_segment_payload.py,sha256=uunxVJS4PE1LojF_UpURMzVK9GuvmYWRqQo_bxEj5TU,3385
|
88
89
|
modal/_utils/function_utils.py,sha256=LgcveUUb4XU_dWxtqgK_3ujZBvS3cGVzcDOkljyFZ2w,25066
|
89
90
|
modal/_utils/grpc_testing.py,sha256=H1zHqthv19eGPJz2HKXDyWXWGSqO4BRsxah3L5Xaa8A,8619
|
90
91
|
modal/_utils/grpc_utils.py,sha256=PPB5ay-vXencXNIWPVw5modr3EH7gfq2QPcO5YJ1lMU,7737
|
91
92
|
modal/_utils/hash_utils.py,sha256=zg3J6OGxTFGSFri1qQ12giDz90lWk8bzaxCTUCRtiX4,3034
|
92
|
-
modal/_utils/http_utils.py,sha256=
|
93
|
+
modal/_utils/http_utils.py,sha256=yeTFsXYr0rYMEhB7vBP7audG9Uc7OLhzKBANFDZWVt0,2451
|
93
94
|
modal/_utils/logger.py,sha256=ePzdudrtx9jJCjuO6-bcL_kwUJfi4AwloUmIiNtqkY0,1330
|
94
95
|
modal/_utils/mount_utils.py,sha256=J-FRZbPQv1i__Tob-FIpbB1oXWpFLAwZiB4OCiJpFG0,3206
|
95
96
|
modal/_utils/name_utils.py,sha256=TW1iyJedvDNPEJ5UVp93u8xuD5J2gQL_CUt1mgov_aI,1939
|
@@ -161,10 +162,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
|
|
161
162
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
162
163
|
modal_version/__init__.py,sha256=RT6zPoOdFO99u5Wcxxaoir4ZCuPTbQ22cvzFAXl3vUY,470
|
163
164
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
164
|
-
modal_version/_version_generated.py,sha256=
|
165
|
-
modal-0.68.
|
166
|
-
modal-0.68.
|
167
|
-
modal-0.68.
|
168
|
-
modal-0.68.
|
169
|
-
modal-0.68.
|
170
|
-
modal-0.68.
|
165
|
+
modal_version/_version_generated.py,sha256=_78tFHpocB2j13QmoHx8RyBXjJbeKL88F2EToSPlNsw,149
|
166
|
+
modal-0.68.16.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
167
|
+
modal-0.68.16.dist-info/METADATA,sha256=5v1HulhtrgeBwxV3CH-IpYMZb07HnnixgU-58vam_ow,2329
|
168
|
+
modal-0.68.16.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
169
|
+
modal-0.68.16.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
170
|
+
modal-0.68.16.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
|
171
|
+
modal-0.68.16.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|