modal 0.73.44__py3-none-any.whl → 0.73.46__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.
@@ -0,0 +1,395 @@
1
+ import asyncio
2
+ import asyncio.locks
3
+ import asyncio.queues
4
+ import collections.abc
5
+ import modal._runtime.user_code_imports
6
+ import modal.client
7
+ import modal_proto.api_pb2
8
+ import synchronicity.combined_types
9
+ import typing
10
+ import typing_extensions
11
+
12
+ class UserException(Exception): ...
13
+ class Sentinel: ...
14
+
15
+ class IOContext:
16
+ input_ids: list[str]
17
+ function_call_ids: list[str]
18
+ finalized_function: modal._runtime.user_code_imports.FinalizedFunction
19
+ _cancel_issued: bool
20
+ _cancel_callback: typing.Optional[collections.abc.Callable[[], None]]
21
+
22
+ def __init__(
23
+ self,
24
+ input_ids: list[str],
25
+ function_call_ids: list[str],
26
+ finalized_function: modal._runtime.user_code_imports.FinalizedFunction,
27
+ function_inputs: list[modal_proto.api_pb2.FunctionInput],
28
+ is_batched: bool,
29
+ client: modal.client._Client,
30
+ ): ...
31
+ @classmethod
32
+ async def create(
33
+ cls,
34
+ client: modal.client._Client,
35
+ finalized_functions: dict[str, modal._runtime.user_code_imports.FinalizedFunction],
36
+ inputs: list[tuple[str, str, modal_proto.api_pb2.FunctionInput]],
37
+ is_batched: bool,
38
+ ) -> IOContext: ...
39
+ def set_cancel_callback(self, cb: collections.abc.Callable[[], None]): ...
40
+ def cancel(self): ...
41
+ def _args_and_kwargs(self) -> tuple[tuple[typing.Any, ...], dict[str, list[typing.Any]]]: ...
42
+ def call_finalized_function(self) -> typing.Any: ...
43
+ def validate_output_data(self, data: typing.Any) -> list[typing.Any]: ...
44
+
45
+ class InputSlots:
46
+ active: int
47
+ value: int
48
+ waiter: typing.Optional[asyncio.Future]
49
+ closed: bool
50
+
51
+ def __init__(self, value: int) -> None: ...
52
+ async def acquire(self) -> None: ...
53
+ def _wake_waiter(self) -> None: ...
54
+ def release(self) -> None: ...
55
+ def set_value(self, value: int) -> None: ...
56
+ async def close(self) -> None: ...
57
+
58
+ class _ContainerIOManager:
59
+ task_id: str
60
+ function_id: str
61
+ app_id: str
62
+ function_def: modal_proto.api_pb2.Function
63
+ checkpoint_id: typing.Optional[str]
64
+ calls_completed: int
65
+ total_user_time: float
66
+ current_input_id: typing.Optional[str]
67
+ current_inputs: dict[str, IOContext]
68
+ current_input_started_at: typing.Optional[float]
69
+ _target_concurrency: int
70
+ _max_concurrency: int
71
+ _concurrency_loop: typing.Optional[asyncio.Task]
72
+ _input_slots: InputSlots
73
+ _environment_name: str
74
+ _heartbeat_loop: typing.Optional[asyncio.Task]
75
+ _heartbeat_condition: typing.Optional[asyncio.locks.Condition]
76
+ _waiting_for_memory_snapshot: bool
77
+ _is_interactivity_enabled: bool
78
+ _fetching_inputs: bool
79
+ _client: modal.client._Client
80
+ _GENERATOR_STOP_SENTINEL: typing.ClassVar[Sentinel]
81
+ _singleton: typing.ClassVar[typing.Optional[_ContainerIOManager]]
82
+
83
+ def _init(self, container_args: modal_proto.api_pb2.ContainerArguments, client: modal.client._Client): ...
84
+ @property
85
+ def heartbeat_condition(self) -> asyncio.locks.Condition: ...
86
+ @staticmethod
87
+ def __new__(
88
+ cls, container_args: modal_proto.api_pb2.ContainerArguments, client: modal.client._Client
89
+ ) -> _ContainerIOManager: ...
90
+ @classmethod
91
+ def _reset_singleton(cls): ...
92
+ async def hello(self): ...
93
+ async def _run_heartbeat_loop(self): ...
94
+ async def _heartbeat_handle_cancellations(self) -> bool: ...
95
+ def heartbeats(self, wait_for_mem_snap: bool) -> typing.AsyncContextManager[None]: ...
96
+ def stop_heartbeat(self): ...
97
+ def dynamic_concurrency_manager(self) -> typing.AsyncContextManager[None]: ...
98
+ async def _dynamic_concurrency_loop(self): ...
99
+ async def get_serialized_function(
100
+ self,
101
+ ) -> tuple[typing.Optional[typing.Any], typing.Optional[collections.abc.Callable[..., typing.Any]]]: ...
102
+ def serialize(self, obj: typing.Any) -> bytes: ...
103
+ def deserialize(self, data: bytes) -> typing.Any: ...
104
+ def serialize_data_format(self, obj: typing.Any, data_format: int) -> bytes: ...
105
+ async def format_blob_data(self, data: bytes) -> dict[str, typing.Any]: ...
106
+ def get_data_in(self, function_call_id: str) -> collections.abc.AsyncIterator[typing.Any]: ...
107
+ async def put_data_out(
108
+ self, function_call_id: str, start_index: int, data_format: int, serialized_messages: list[typing.Any]
109
+ ) -> None: ...
110
+ async def generator_output_task(
111
+ self, function_call_id: str, data_format: int, message_rx: asyncio.queues.Queue
112
+ ) -> None: ...
113
+ async def _queue_create(self, size: int) -> asyncio.queues.Queue: ...
114
+ async def _queue_put(self, queue: asyncio.queues.Queue, value: typing.Any) -> None: ...
115
+ def get_average_call_time(self) -> float: ...
116
+ def get_max_inputs_to_fetch(self): ...
117
+ def _generate_inputs(
118
+ self, batch_max_size: int, batch_wait_ms: int
119
+ ) -> collections.abc.AsyncIterator[list[tuple[str, str, modal_proto.api_pb2.FunctionInput]]]: ...
120
+ def run_inputs_outputs(
121
+ self,
122
+ finalized_functions: dict[str, modal._runtime.user_code_imports.FinalizedFunction],
123
+ batch_max_size: int = 0,
124
+ batch_wait_ms: int = 0,
125
+ ) -> collections.abc.AsyncIterator[IOContext]: ...
126
+ async def _push_outputs(
127
+ self,
128
+ io_context: IOContext,
129
+ started_at: float,
130
+ data_format: int,
131
+ results: list[modal_proto.api_pb2.GenericResult],
132
+ ) -> None: ...
133
+ def serialize_exception(self, exc: BaseException) -> bytes: ...
134
+ def serialize_traceback(self, exc: BaseException) -> tuple[typing.Optional[bytes], typing.Optional[bytes]]: ...
135
+ def handle_user_exception(self) -> typing.AsyncContextManager[None]: ...
136
+ def handle_input_exception(self, io_context: IOContext, started_at: float) -> typing.AsyncContextManager[None]: ...
137
+ def exit_context(self, started_at, input_ids: list[str]): ...
138
+ async def push_outputs(
139
+ self, io_context: IOContext, started_at: float, data: typing.Any, data_format: int
140
+ ) -> None: ...
141
+ async def memory_restore(self) -> None: ...
142
+ async def memory_snapshot(self) -> None: ...
143
+ async def volume_commit(self, volume_ids: list[str]) -> None: ...
144
+ async def interact(self, from_breakpoint: bool = False): ...
145
+ @property
146
+ def target_concurrency(self) -> int: ...
147
+ @property
148
+ def max_concurrency(self) -> int: ...
149
+ @classmethod
150
+ def get_input_concurrency(cls) -> int: ...
151
+ @classmethod
152
+ def set_input_concurrency(cls, concurrency: int): ...
153
+ @classmethod
154
+ def stop_fetching_inputs(cls): ...
155
+
156
+ SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
157
+
158
+ class ContainerIOManager:
159
+ task_id: str
160
+ function_id: str
161
+ app_id: str
162
+ function_def: modal_proto.api_pb2.Function
163
+ checkpoint_id: typing.Optional[str]
164
+ calls_completed: int
165
+ total_user_time: float
166
+ current_input_id: typing.Optional[str]
167
+ current_inputs: dict[str, IOContext]
168
+ current_input_started_at: typing.Optional[float]
169
+ _target_concurrency: int
170
+ _max_concurrency: int
171
+ _concurrency_loop: typing.Optional[asyncio.Task]
172
+ _input_slots: InputSlots
173
+ _environment_name: str
174
+ _heartbeat_loop: typing.Optional[asyncio.Task]
175
+ _heartbeat_condition: typing.Optional[asyncio.locks.Condition]
176
+ _waiting_for_memory_snapshot: bool
177
+ _is_interactivity_enabled: bool
178
+ _fetching_inputs: bool
179
+ _client: modal.client.Client
180
+ _GENERATOR_STOP_SENTINEL: typing.ClassVar[Sentinel]
181
+ _singleton: typing.ClassVar[typing.Optional[ContainerIOManager]]
182
+
183
+ def __init__(self, /, *args, **kwargs): ...
184
+ def _init(self, container_args: modal_proto.api_pb2.ContainerArguments, client: modal.client.Client): ...
185
+ @property
186
+ def heartbeat_condition(self) -> asyncio.locks.Condition: ...
187
+ @classmethod
188
+ def _reset_singleton(cls): ...
189
+
190
+ class __hello_spec(typing_extensions.Protocol[SUPERSELF]):
191
+ def __call__(self): ...
192
+ async def aio(self): ...
193
+
194
+ hello: __hello_spec[typing_extensions.Self]
195
+
196
+ class ___run_heartbeat_loop_spec(typing_extensions.Protocol[SUPERSELF]):
197
+ def __call__(self): ...
198
+ async def aio(self): ...
199
+
200
+ _run_heartbeat_loop: ___run_heartbeat_loop_spec[typing_extensions.Self]
201
+
202
+ class ___heartbeat_handle_cancellations_spec(typing_extensions.Protocol[SUPERSELF]):
203
+ def __call__(self) -> bool: ...
204
+ async def aio(self) -> bool: ...
205
+
206
+ _heartbeat_handle_cancellations: ___heartbeat_handle_cancellations_spec[typing_extensions.Self]
207
+
208
+ class __heartbeats_spec(typing_extensions.Protocol[SUPERSELF]):
209
+ def __call__(
210
+ self, wait_for_mem_snap: bool
211
+ ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[None]: ...
212
+ def aio(self, wait_for_mem_snap: bool) -> typing.AsyncContextManager[None]: ...
213
+
214
+ heartbeats: __heartbeats_spec[typing_extensions.Self]
215
+
216
+ def stop_heartbeat(self): ...
217
+
218
+ class __dynamic_concurrency_manager_spec(typing_extensions.Protocol[SUPERSELF]):
219
+ def __call__(self) -> synchronicity.combined_types.AsyncAndBlockingContextManager[None]: ...
220
+ def aio(self) -> typing.AsyncContextManager[None]: ...
221
+
222
+ dynamic_concurrency_manager: __dynamic_concurrency_manager_spec[typing_extensions.Self]
223
+
224
+ class ___dynamic_concurrency_loop_spec(typing_extensions.Protocol[SUPERSELF]):
225
+ def __call__(self): ...
226
+ async def aio(self): ...
227
+
228
+ _dynamic_concurrency_loop: ___dynamic_concurrency_loop_spec[typing_extensions.Self]
229
+
230
+ class __get_serialized_function_spec(typing_extensions.Protocol[SUPERSELF]):
231
+ def __call__(
232
+ self,
233
+ ) -> tuple[typing.Optional[typing.Any], typing.Optional[collections.abc.Callable[..., typing.Any]]]: ...
234
+ async def aio(
235
+ self,
236
+ ) -> tuple[typing.Optional[typing.Any], typing.Optional[collections.abc.Callable[..., typing.Any]]]: ...
237
+
238
+ get_serialized_function: __get_serialized_function_spec[typing_extensions.Self]
239
+
240
+ def serialize(self, obj: typing.Any) -> bytes: ...
241
+ def deserialize(self, data: bytes) -> typing.Any: ...
242
+ def serialize_data_format(self, obj: typing.Any, data_format: int) -> bytes: ...
243
+
244
+ class __format_blob_data_spec(typing_extensions.Protocol[SUPERSELF]):
245
+ def __call__(self, data: bytes) -> dict[str, typing.Any]: ...
246
+ async def aio(self, data: bytes) -> dict[str, typing.Any]: ...
247
+
248
+ format_blob_data: __format_blob_data_spec[typing_extensions.Self]
249
+
250
+ class __get_data_in_spec(typing_extensions.Protocol[SUPERSELF]):
251
+ def __call__(self, function_call_id: str) -> typing.Iterator[typing.Any]: ...
252
+ def aio(self, function_call_id: str) -> collections.abc.AsyncIterator[typing.Any]: ...
253
+
254
+ get_data_in: __get_data_in_spec[typing_extensions.Self]
255
+
256
+ class __put_data_out_spec(typing_extensions.Protocol[SUPERSELF]):
257
+ def __call__(
258
+ self, function_call_id: str, start_index: int, data_format: int, serialized_messages: list[typing.Any]
259
+ ) -> None: ...
260
+ async def aio(
261
+ self, function_call_id: str, start_index: int, data_format: int, serialized_messages: list[typing.Any]
262
+ ) -> None: ...
263
+
264
+ put_data_out: __put_data_out_spec[typing_extensions.Self]
265
+
266
+ class __generator_output_task_spec(typing_extensions.Protocol[SUPERSELF]):
267
+ def __call__(self, function_call_id: str, data_format: int, message_rx: asyncio.queues.Queue) -> None: ...
268
+ async def aio(self, function_call_id: str, data_format: int, message_rx: asyncio.queues.Queue) -> None: ...
269
+
270
+ generator_output_task: __generator_output_task_spec[typing_extensions.Self]
271
+
272
+ class ___queue_create_spec(typing_extensions.Protocol[SUPERSELF]):
273
+ def __call__(self, size: int) -> asyncio.queues.Queue: ...
274
+ async def aio(self, size: int) -> asyncio.queues.Queue: ...
275
+
276
+ _queue_create: ___queue_create_spec[typing_extensions.Self]
277
+
278
+ class ___queue_put_spec(typing_extensions.Protocol[SUPERSELF]):
279
+ def __call__(self, queue: asyncio.queues.Queue, value: typing.Any) -> None: ...
280
+ async def aio(self, queue: asyncio.queues.Queue, value: typing.Any) -> None: ...
281
+
282
+ _queue_put: ___queue_put_spec[typing_extensions.Self]
283
+
284
+ def get_average_call_time(self) -> float: ...
285
+ def get_max_inputs_to_fetch(self): ...
286
+
287
+ class ___generate_inputs_spec(typing_extensions.Protocol[SUPERSELF]):
288
+ def __call__(
289
+ self, batch_max_size: int, batch_wait_ms: int
290
+ ) -> typing.Iterator[list[tuple[str, str, modal_proto.api_pb2.FunctionInput]]]: ...
291
+ def aio(
292
+ self, batch_max_size: int, batch_wait_ms: int
293
+ ) -> collections.abc.AsyncIterator[list[tuple[str, str, modal_proto.api_pb2.FunctionInput]]]: ...
294
+
295
+ _generate_inputs: ___generate_inputs_spec[typing_extensions.Self]
296
+
297
+ class __run_inputs_outputs_spec(typing_extensions.Protocol[SUPERSELF]):
298
+ def __call__(
299
+ self,
300
+ finalized_functions: dict[str, modal._runtime.user_code_imports.FinalizedFunction],
301
+ batch_max_size: int = 0,
302
+ batch_wait_ms: int = 0,
303
+ ) -> typing.Iterator[IOContext]: ...
304
+ def aio(
305
+ self,
306
+ finalized_functions: dict[str, modal._runtime.user_code_imports.FinalizedFunction],
307
+ batch_max_size: int = 0,
308
+ batch_wait_ms: int = 0,
309
+ ) -> collections.abc.AsyncIterator[IOContext]: ...
310
+
311
+ run_inputs_outputs: __run_inputs_outputs_spec[typing_extensions.Self]
312
+
313
+ class ___push_outputs_spec(typing_extensions.Protocol[SUPERSELF]):
314
+ def __call__(
315
+ self,
316
+ io_context: IOContext,
317
+ started_at: float,
318
+ data_format: int,
319
+ results: list[modal_proto.api_pb2.GenericResult],
320
+ ) -> None: ...
321
+ async def aio(
322
+ self,
323
+ io_context: IOContext,
324
+ started_at: float,
325
+ data_format: int,
326
+ results: list[modal_proto.api_pb2.GenericResult],
327
+ ) -> None: ...
328
+
329
+ _push_outputs: ___push_outputs_spec[typing_extensions.Self]
330
+
331
+ def serialize_exception(self, exc: BaseException) -> bytes: ...
332
+ def serialize_traceback(self, exc: BaseException) -> tuple[typing.Optional[bytes], typing.Optional[bytes]]: ...
333
+
334
+ class __handle_user_exception_spec(typing_extensions.Protocol[SUPERSELF]):
335
+ def __call__(self) -> synchronicity.combined_types.AsyncAndBlockingContextManager[None]: ...
336
+ def aio(self) -> typing.AsyncContextManager[None]: ...
337
+
338
+ handle_user_exception: __handle_user_exception_spec[typing_extensions.Self]
339
+
340
+ class __handle_input_exception_spec(typing_extensions.Protocol[SUPERSELF]):
341
+ def __call__(
342
+ self, io_context: IOContext, started_at: float
343
+ ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[None]: ...
344
+ def aio(self, io_context: IOContext, started_at: float) -> typing.AsyncContextManager[None]: ...
345
+
346
+ handle_input_exception: __handle_input_exception_spec[typing_extensions.Self]
347
+
348
+ def exit_context(self, started_at, input_ids: list[str]): ...
349
+
350
+ class __push_outputs_spec(typing_extensions.Protocol[SUPERSELF]):
351
+ def __call__(self, io_context: IOContext, started_at: float, data: typing.Any, data_format: int) -> None: ...
352
+ async def aio(self, io_context: IOContext, started_at: float, data: typing.Any, data_format: int) -> None: ...
353
+
354
+ push_outputs: __push_outputs_spec[typing_extensions.Self]
355
+
356
+ class __memory_restore_spec(typing_extensions.Protocol[SUPERSELF]):
357
+ def __call__(self) -> None: ...
358
+ async def aio(self) -> None: ...
359
+
360
+ memory_restore: __memory_restore_spec[typing_extensions.Self]
361
+
362
+ class __memory_snapshot_spec(typing_extensions.Protocol[SUPERSELF]):
363
+ def __call__(self) -> None: ...
364
+ async def aio(self) -> None: ...
365
+
366
+ memory_snapshot: __memory_snapshot_spec[typing_extensions.Self]
367
+
368
+ class __volume_commit_spec(typing_extensions.Protocol[SUPERSELF]):
369
+ def __call__(self, volume_ids: list[str]) -> None: ...
370
+ async def aio(self, volume_ids: list[str]) -> None: ...
371
+
372
+ volume_commit: __volume_commit_spec[typing_extensions.Self]
373
+
374
+ class __interact_spec(typing_extensions.Protocol[SUPERSELF]):
375
+ def __call__(self, from_breakpoint: bool = False): ...
376
+ async def aio(self, from_breakpoint: bool = False): ...
377
+
378
+ interact: __interact_spec[typing_extensions.Self]
379
+
380
+ @property
381
+ def target_concurrency(self) -> int: ...
382
+ @property
383
+ def max_concurrency(self) -> int: ...
384
+ @classmethod
385
+ def get_input_concurrency(cls) -> int: ...
386
+ @classmethod
387
+ def set_input_concurrency(cls, concurrency: int): ...
388
+ @classmethod
389
+ def stop_fetching_inputs(cls): ...
390
+
391
+ def check_fastapi_pydantic_compatibility(exc: ImportError) -> None: ...
392
+
393
+ MAX_OUTPUT_BATCH_SIZE: int
394
+
395
+ RTT_S: float
@@ -0,0 +1,23 @@
1
+ import collections.abc
2
+ import contextvars
3
+ import typing
4
+ import typing_extensions
5
+
6
+ def is_local() -> bool: ...
7
+ async def _interact() -> None: ...
8
+
9
+ class __interact_spec(typing_extensions.Protocol):
10
+ def __call__(self) -> None: ...
11
+ async def aio(self) -> None: ...
12
+
13
+ interact: __interact_spec
14
+
15
+ def current_input_id() -> typing.Optional[str]: ...
16
+ def current_function_call_id() -> typing.Optional[str]: ...
17
+ def _set_current_context_ids(
18
+ input_ids: list[str], function_call_ids: list[str]
19
+ ) -> collections.abc.Callable[[], None]: ...
20
+
21
+ _current_input_id: contextvars.ContextVar
22
+
23
+ _current_function_call_id: contextvars.ContextVar
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.44"
30
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.46"
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.44"
88
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.46"
89
89
  ): ...
90
90
  def is_closed(self) -> bool: ...
91
91
  @property
@@ -1,11 +1,13 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: modal
3
- Version: 0.73.44
3
+ Version: 0.73.46
4
4
  Summary: Python client library for Modal
5
- Author: Modal Labs
6
- Author-email: support@modal.com
5
+ Author-email: Modal Labs <support@modal.com>
6
+ License: Apache-2.0
7
7
  Project-URL: Homepage, https://modal.com
8
8
  Project-URL: Source, https://github.com/modal-labs/modal-client
9
+ Project-URL: Documentation, https://modal.com/docs
10
+ Project-URL: Issue Tracker, https://github.com/modal-labs/modal-client/issues
9
11
  Keywords: modal,client,cloud,serverless,infrastructure
10
12
  Classifier: Topic :: System :: Distributed Computing
11
13
  Classifier: Operating System :: OS Independent
@@ -16,18 +18,18 @@ Description-Content-Type: text/markdown
16
18
  License-File: LICENSE
17
19
  Requires-Dist: aiohttp
18
20
  Requires-Dist: certifi
19
- Requires-Dist: click (>=8.1.0)
21
+ Requires-Dist: click>=8.1.0
20
22
  Requires-Dist: fastapi
21
- Requires-Dist: grpclib (==0.4.7)
22
- Requires-Dist: protobuf (!=4.24.0,<6.0,>=3.19)
23
- Requires-Dist: rich (>=12.0.0)
24
- Requires-Dist: synchronicity (~=0.9.10)
23
+ Requires-Dist: grpclib==0.4.7
24
+ Requires-Dist: protobuf!=4.24.0,<6.0,>=3.19
25
+ Requires-Dist: rich>=12.0.0
26
+ Requires-Dist: synchronicity~=0.9.10
25
27
  Requires-Dist: toml
26
- Requires-Dist: typer (>=0.9)
28
+ Requires-Dist: typer>=0.9
27
29
  Requires-Dist: types-certifi
28
30
  Requires-Dist: types-toml
29
31
  Requires-Dist: watchfiles
30
- Requires-Dist: typing-extensions (~=4.6)
32
+ Requires-Dist: typing_extensions~=4.6
31
33
 
32
34
  # Modal Python Library
33
35
 
@@ -22,7 +22,7 @@ modal/app.py,sha256=rCOPD51gVyow8muyaqMuV65qfTnAZKf_w1OCZdSF_6o,44636
22
22
  modal/app.pyi,sha256=0MMCgskIL4r3eq8oBcfm2lLyeao2gXjS3iXaIfmaJ-o,25959
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=8SQawr7P1PNUCq1UmJMUQXG2jIo4Nmdcs311XqrNLRE,15276
25
- modal/client.pyi,sha256=Us7guqm-7JE3H9hipPxqhyOrFsJz2nbspDc03c4JmAA,7593
25
+ modal/client.pyi,sha256=O1FkvafYE0HPYhNlOAfgFPBfi1-ZQ00hbjPGOtk2PAk,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
28
  modal/cls.py,sha256=nJhJr-YuttOmdNWxtMHcBV-q6iQmY5qXkEy9yO43clY,31130
@@ -85,7 +85,9 @@ modal/volume.pyi,sha256=kTsXarphjZILXci84LQy7EyC84eXUs5-7D62IM5q3eE,12491
85
85
  modal/_runtime/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
86
86
  modal/_runtime/asgi.py,sha256=vIxpGrCZhdeThwazQckmrqoNKgDQYOyv8emzBHr8CiU,22154
87
87
  modal/_runtime/container_io_manager.py,sha256=3VPj0RWtSdvVZD96l5ZpO8MjhLRjAi_P_ZtpNK3nGn0,43593
88
+ modal/_runtime/container_io_manager.pyi,sha256=sEGpOvlDOQCwUiJn4CAyE8TT2lCBKNBEqGWExfvVKsY,16661
88
89
  modal/_runtime/execution_context.py,sha256=E6ofm6j1POXGPxS841X3V7JU6NheVb8OkQc7JpLq4Kg,2712
90
+ modal/_runtime/execution_context.pyi,sha256=wQZwMNADExkeNdB9yKX0PPojovxlFHbap3441wAsiMY,634
89
91
  modal/_runtime/gpu_memory_snapshot.py,sha256=tA3m1d1cwnmHpvpCeN_WijDd6n8byn7LWlpicbIxiOI,3144
90
92
  modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5163
91
93
  modal/_runtime/user_code_imports.py,sha256=qDasIvffN3SJjUeT1DHcpN35lWNepnbomS0Z8yD65mA,14740
@@ -142,18 +144,6 @@ modal/requirements/2024.04.txt,sha256=6NnrbIE-mflwMyKyQ0tsWeY8XFE1kSW9oE8DVDoD8Q
142
144
  modal/requirements/2024.10.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddRo,296
143
145
  modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
144
146
  modal/requirements/base-images.json,sha256=kLNo5Sqmnhp9H6Hr9IcaGJFrRaRg1yfuepUWkm-y8iQ,571
145
- modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
146
- modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
147
- modal_docs/gen_reference_docs.py,sha256=aDcUSSDtAAZ4eeFWyroeIg2TOzyRoYcic-d9Zh9TdLY,6656
148
- modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
149
- modal_docs/mdmd/mdmd.py,sha256=Irx49MCCTlBOP4FBdLR--JrpA3-WhsVeriq0LGgsRic,6232
150
- modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
151
- modal_global_objects/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
152
- modal_global_objects/images/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
153
- modal_global_objects/images/base_images.py,sha256=tFc7tzQRJHtq23kURd6DTrnnO4Yp5ujr34WdJOM5ubI,775
154
- modal_global_objects/mounts/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
155
- modal_global_objects/mounts/modal_client_package.py,sha256=W0E_yShsRojPzWm6LtIQqNVolapdnrZkm2hVEQuZK_4,767
156
- modal_global_objects/mounts/python_standalone.py,sha256=EsC-hdPtiAPOwgW9emHN6muNUkrJwR8dYxroVArxHxM,1841
157
147
  modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
158
148
  modal_proto/api.proto,sha256=hM-iLwpYycp-wGyvoAieAEM_km42yI0jZTigPQLGPC0,86712
159
149
  modal_proto/api_grpc.py,sha256=FYGqDegM_w_qxdtlxum8k31mDibKoMvmNxv_p9cKdKs,109056
@@ -172,10 +162,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
172
162
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
163
  modal_version/__init__.py,sha256=wiJQ53c-OMs0Xf1UeXOxQ7FwlV1VzIjnX6o-pRYZ_Pk,470
174
164
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
175
- modal_version/_version_generated.py,sha256=Z2EVtAHyRHPl9O8NnhkBr_IETVMlMSkE6X0LVKvJnCw,149
176
- modal-0.73.44.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
177
- modal-0.73.44.dist-info/METADATA,sha256=Li_yB2IQE6WhbqRbnksCXzgcHgninuSVyJXtRJ6Fxno,2330
178
- modal-0.73.44.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
179
- modal-0.73.44.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
180
- modal-0.73.44.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
181
- modal-0.73.44.dist-info/RECORD,,
165
+ modal_version/_version_generated.py,sha256=8fsq1Mwk_oc-cRzDa1hVjelGQTPb2sWXcU1-oVbyluE,149
166
+ modal-0.73.46.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
167
+ modal-0.73.46.dist-info/METADATA,sha256=cRFzas4CsmfLqOAtoQuAAfiB1dKGnRUifH9e0hUWVwg,2452
168
+ modal-0.73.46.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
169
+ modal-0.73.46.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
170
+ modal-0.73.46.dist-info/top_level.txt,sha256=UkI9BtusWSFRKv6hePUkcXTROTUz7xo7rKGg_IHjuBM,32
171
+ modal-0.73.46.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.37.1)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,5 +1,3 @@
1
1
  modal
2
- modal_docs
3
- modal_global_objects
4
2
  modal_proto
5
3
  modal_version
@@ -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 = 44 # git: 65ca596
4
+ build_number = 46 # git: 8736584
modal_docs/__init__.py DELETED
@@ -1 +0,0 @@
1
- # Copyright Modal Labs 2023
@@ -1,117 +0,0 @@
1
- # Copyright Modal Labs 2023
2
- import inspect
3
- import sys
4
- from pathlib import Path
5
- from typing import Optional, cast
6
-
7
- from click import Command, Context, Group
8
-
9
- from modal.cli.entry_point import entrypoint_cli
10
-
11
-
12
- # Adapted from typer_cli, since it's incompatible with the latest version of typer
13
- # (see https://github.com/tiangolo/typer-cli/issues/50)
14
- def get_docs_for_click(
15
- obj: Command,
16
- ctx: Context,
17
- *,
18
- indent: int = 0,
19
- name: str = "",
20
- call_prefix: str = "",
21
- ) -> str:
22
- docs = "#" * (1 + indent)
23
- command_name = name or obj.name
24
- if call_prefix:
25
- command_name = f"{call_prefix} {command_name}"
26
- title = f"`{command_name}`" if command_name else "CLI"
27
- docs += f" {title}\n\n"
28
- if obj.help:
29
- docs += f"{inspect.cleandoc(obj.help)}\n\n"
30
- usage_pieces = obj.collect_usage_pieces(ctx)
31
- if usage_pieces:
32
- docs += "**Usage**:\n\n"
33
- docs += "```shell\n"
34
- if command_name:
35
- docs += f"{command_name} "
36
- docs += f"{' '.join(usage_pieces)}\n"
37
- docs += "```\n\n"
38
- args = []
39
- opts = []
40
- for param in obj.get_params(ctx):
41
- rv = param.get_help_record(ctx)
42
- if rv is not None:
43
- if getattr(param, "hidden", False):
44
- continue
45
- if param.param_type_name == "argument":
46
- args.append(rv)
47
- elif param.param_type_name == "option":
48
- opts.append(rv)
49
- if args:
50
- docs += "**Arguments**:\n\n"
51
- for arg_name, arg_help in args:
52
- docs += f"* `{arg_name}`"
53
- if arg_help:
54
- docs += f": {arg_help}"
55
- docs += "\n"
56
- docs += "\n"
57
- if opts:
58
- docs += "**Options**:\n\n"
59
- for opt_name, opt_help in opts:
60
- docs += f"* `{opt_name}`"
61
- if opt_help:
62
- docs += f": {opt_help}"
63
- docs += "\n"
64
- docs += "\n"
65
- if obj.epilog:
66
- docs += f"{obj.epilog}\n\n"
67
- if isinstance(obj, Group):
68
- group: Group = cast(Group, obj)
69
- commands = group.list_commands(ctx)
70
- if commands:
71
- docs += "**Commands**:\n\n"
72
- for command in commands:
73
- command_obj = group.get_command(ctx, command)
74
- assert command_obj
75
- if command_obj.hidden:
76
- continue
77
- docs += f"* `{command_obj.name}`"
78
- command_help = command_obj.get_short_help_str(limit=250)
79
- if command_help:
80
- docs += f": {command_help}"
81
- docs += "\n"
82
- docs += "\n"
83
- for command in commands:
84
- command_obj = group.get_command(ctx, command)
85
- if command_obj.hidden:
86
- continue
87
- assert command_obj
88
- use_prefix = ""
89
- if command_name:
90
- use_prefix += f"{command_name}"
91
- docs += get_docs_for_click(obj=command_obj, ctx=ctx, indent=indent + 1, call_prefix=use_prefix)
92
- return docs
93
-
94
-
95
- def run(output_dirname: Optional[str]) -> None:
96
- entrypoint: Group = cast(Group, entrypoint_cli)
97
- ctx = Context(entrypoint)
98
- commands = entrypoint.list_commands(ctx)
99
-
100
- for command in commands:
101
- command_obj = entrypoint.get_command(ctx, command)
102
- if command_obj.hidden:
103
- continue
104
- docs = get_docs_for_click(obj=command_obj, ctx=ctx, call_prefix="modal")
105
-
106
- if output_dirname:
107
- output_dir = Path(output_dirname)
108
- output_dir.mkdir(parents=True, exist_ok=True)
109
- output_file = output_dir / f"{command}.md"
110
- print("Writing to", output_file)
111
- output_file.write_text(docs)
112
- else:
113
- print(docs)
114
-
115
-
116
- if __name__ == "__main__":
117
- run(None if len(sys.argv) <= 1 else sys.argv[1])
@@ -1,193 +0,0 @@
1
- # Copyright Modal Labs 2023
2
- import importlib
3
- import inspect
4
- import json
5
- import os
6
- import sys
7
- import warnings
8
- from typing import NamedTuple
9
-
10
- from synchronicity.synchronizer import FunctionWithAio
11
-
12
- from .mdmd.mdmd import (
13
- Category,
14
- class_str,
15
- default_filter,
16
- function_str,
17
- module_items,
18
- module_str,
19
- object_is_private,
20
- package_filter,
21
- )
22
-
23
-
24
- class DocItem(NamedTuple):
25
- label: str
26
- category: Category
27
- document: str
28
- in_sidebar: bool = True
29
-
30
-
31
- def validate_doc_item(docitem: DocItem) -> DocItem:
32
- # Check that unwanted strings aren't leaking into our docs.
33
- bad_strings = [
34
- # Presence of a to-do inside a `DocItem` usually indicates it's been
35
- # placed inside a function signature definition or right underneath it, before the body.
36
- # Fix by moving the to-do into the body or above the signature.
37
- "TODO:"
38
- ]
39
- for line in docitem.document.splitlines():
40
- for bad_str in bad_strings:
41
- if bad_str in line:
42
- msg = f"Found unwanted string '{bad_str}' in content for item '{docitem.label}'. Problem line: {line}"
43
- raise ValueError(msg)
44
- return docitem
45
-
46
-
47
- def run(output_dir: str = None):
48
- """Generate Modal docs."""
49
- import modal
50
-
51
- ordered_doc_items: list[DocItem] = []
52
- documented_items = set()
53
-
54
- def filter_non_aio(module, name):
55
- return not name.lower().startswith("aio")
56
-
57
- def filter_already_documented(module, name):
58
- item = getattr(module, name)
59
- try:
60
- if item in documented_items:
61
- return False
62
- except TypeError: # unhashable stuff
63
- print(f"Warning: could not document item {name}: {item}:")
64
- return False
65
- documented_items.add(item)
66
- return True
67
-
68
- def modal_default_filter(module, name):
69
- return default_filter(module, name) and filter_non_aio(module, name) and filter_already_documented(module, name)
70
-
71
- def top_level_filter(module, name):
72
- item = getattr(module, name)
73
- if object_is_private(name, item) or inspect.ismodule(item):
74
- return False
75
- return package_filter("modal") and filter_already_documented(module, name) and filter_non_aio(module, name)
76
-
77
- base_title_level = "#"
78
- forced_module_docs = [
79
- ("modal.Function", "modal.functions"),
80
- ("modal.Secret", "modal.secret"),
81
- ("modal.Dict", "modal.dict"),
82
- ("modal.Queue", "modal.queue"),
83
- ("modal.call_graph", "modal.call_graph"),
84
- ("modal.gpu", "modal.gpu"),
85
- ("modal.runner", "modal.runner"),
86
- ("modal.Sandbox", "modal.sandbox"),
87
- ("modal.ContainerProcess", "modal.container_process"),
88
- ("modal.io_streams", "modal.io_streams"),
89
- ("modal.FileIO", "modal.file_io"),
90
- ]
91
- # These aren't defined in `modal`, but should still be documented as top-level entries.
92
- forced_members = {"web_endpoint", "asgi_app", "method", "wsgi_app", "forward"}
93
- # These are excluded from the sidebar, typically to 'soft release' some documentation.
94
- sidebar_excluded: set[str] = set()
95
-
96
- for title, modulepath in forced_module_docs:
97
- module = importlib.import_module(modulepath)
98
- document = module_str(modulepath, module, title_level=base_title_level, filter_items=modal_default_filter)
99
- if document:
100
- ordered_doc_items.append(
101
- validate_doc_item(
102
- DocItem(
103
- label=title,
104
- category=Category.MODULE,
105
- document=document,
106
- in_sidebar=title not in sidebar_excluded,
107
- )
108
- )
109
- )
110
-
111
- def f(module, member_name):
112
- return top_level_filter(module, member_name) or (member_name in forced_members)
113
-
114
- # now add all remaining top level modal.X entries
115
- for qual_name, item_name, item in module_items(modal, filter_items=f):
116
- if object_is_private(item_name, item):
117
- continue # skip stuff that's part of explicit `handle_objects` above
118
-
119
- title = f"modal.{item_name}"
120
- if inspect.isclass(item):
121
- content = f"{base_title_level} {qual_name}\n\n" + class_str(item_name, item, base_title_level)
122
- category = Category.CLASS
123
- elif inspect.isroutine(item) or isinstance(item, FunctionWithAio):
124
- content = f"{base_title_level} {qual_name}\n\n" + function_str(item_name, item)
125
- category = Category.FUNCTION
126
- elif inspect.ismodule(item):
127
- continue # skipping imported modules
128
- else:
129
- warnings.warn(f"Not sure how to document: {item_name} ({item})")
130
- continue
131
- ordered_doc_items.append(
132
- validate_doc_item(
133
- DocItem(
134
- label=title,
135
- category=category,
136
- document=content,
137
- in_sidebar=title not in sidebar_excluded,
138
- )
139
- )
140
- )
141
- ordered_doc_items.sort()
142
-
143
- for modulepath in ["modal.exception", "modal.config"]:
144
- module = importlib.import_module(modulepath)
145
- document = module_str(modulepath, module, title_level=base_title_level, filter_items=modal_default_filter)
146
- ordered_doc_items.append(
147
- DocItem(
148
- label=modulepath,
149
- category=Category.MODULE,
150
- document=document,
151
- )
152
- )
153
-
154
- # TODO: add some way of documenting our .aio sub-methods
155
-
156
- make_markdown_docs(
157
- ordered_doc_items,
158
- output_dir,
159
- )
160
-
161
-
162
- def make_markdown_docs(items: list[DocItem], output_dir: str = None):
163
- def _write_file(rel_path: str, data: str):
164
- if output_dir is None:
165
- print(f"<<< {rel_path}")
166
- print(data)
167
- print(f">>> {rel_path}")
168
- return
169
-
170
- filename = os.path.join(output_dir, rel_path)
171
- print("Writing to", filename)
172
- os.makedirs(os.path.dirname(filename), exist_ok=True)
173
- with open(filename, "w") as fp:
174
- fp.write(data)
175
-
176
- sidebar_items = []
177
- for item in items:
178
- if item.in_sidebar:
179
- sidebar_items.append(
180
- {
181
- "label": item.label,
182
- "category": item.category.value,
183
- }
184
- )
185
- _write_file(f"{item.label}.md", item.document)
186
-
187
- sidebar_data = {"items": sidebar_items}
188
- _write_file("sidebar.json", json.dumps(sidebar_data))
189
-
190
-
191
- if __name__ == "__main__":
192
- # running this module outputs docs to stdout for inspection, useful for debugging
193
- run(None if len(sys.argv) <= 1 else sys.argv[1])
@@ -1 +0,0 @@
1
- # Copyright Modal Labs 2023
modal_docs/mdmd/mdmd.py DELETED
@@ -1,195 +0,0 @@
1
- # Copyright Modal Labs 2023
2
- """mdmd - MoDal MarkDown"""
3
-
4
- import inspect
5
- import warnings
6
- from enum import Enum, EnumMeta
7
- from types import ModuleType
8
- from typing import Callable
9
-
10
- import synchronicity.synchronizer
11
-
12
- from .signatures import get_signature
13
-
14
-
15
- def format_docstring(docstring: str):
16
- if docstring is None:
17
- docstring = ""
18
- else:
19
- docstring = inspect.cleandoc(docstring)
20
-
21
- if docstring and not docstring.endswith("\n"):
22
- docstring += "\n"
23
-
24
- return docstring
25
-
26
-
27
- def function_str(name: str, func):
28
- signature = get_signature(name, func)
29
- decl = f"""```python
30
- {signature}
31
- ```\n\n"""
32
- docstring = format_docstring(func.__doc__)
33
- return decl + docstring
34
-
35
-
36
- def class_str(name, obj, title_level="##"):
37
- def qual_name(cls):
38
- if cls.__module__ == "builtins":
39
- return cls.__name__
40
- return f"{cls.__module__}.{cls.__name__}"
41
-
42
- bases = [qual_name(b) for b in obj.__bases__]
43
- bases_str = f"({', '.join(bases)})" if bases else ""
44
- decl = f"""```python
45
- class {name}{bases_str}
46
- ```\n\n"""
47
- parts = [decl]
48
- docstring = format_docstring(obj.__doc__)
49
-
50
- if isinstance(obj, EnumMeta) and not docstring:
51
- # Python 3.11 removed the docstring from enums
52
- docstring = "An enumeration.\n"
53
-
54
- if docstring:
55
- parts.append(docstring + "\n")
56
-
57
- if isinstance(obj, EnumMeta):
58
- enum_vals = "\n".join(f"* `{k}`" for k in obj.__members__.keys())
59
- parts.append(f"The possible values are:\n\n{enum_vals}\n")
60
-
61
- else:
62
- init = inspect.unwrap(obj.__init__)
63
-
64
- if (inspect.isfunction(init) or inspect.ismethod(init)) and not object_is_private("constructor", init):
65
- parts.append(function_str("__init__", init))
66
-
67
- member_title_level = title_level + "#"
68
-
69
- entries = {}
70
-
71
- def rec_update_attributes(cls):
72
- # first bases, then class itself
73
- for base_cls in cls.__bases__:
74
- rec_update_attributes(base_cls)
75
- entries.update(cls.__dict__)
76
-
77
- rec_update_attributes(obj)
78
-
79
- for member_name, member in entries.items():
80
- if isinstance(member, classmethod) or isinstance(member, staticmethod):
81
- # get the original function definition instead of the descriptor object
82
- member = getattr(obj, member_name)
83
- elif isinstance(member, property):
84
- member = member.fget
85
- elif isinstance(member, (synchronicity.synchronizer.FunctionWithAio, synchronicity.synchronizer.MethodWithAio)):
86
- member = member._func
87
-
88
- if object_is_private(member_name, member):
89
- continue
90
-
91
- if callable(member):
92
- parts.append(f"{member_title_level} {member_name}\n\n")
93
- parts.append(function_str(member_name, member))
94
-
95
- return "".join(parts)
96
-
97
-
98
- def module_str(header, module, title_level="#", filter_items: Callable[[ModuleType, str], bool] = None):
99
- header = [f"{title_level} {header}\n\n"]
100
- docstring = format_docstring(module.__doc__)
101
- if docstring:
102
- header.append(docstring + "\n")
103
-
104
- object_docs = []
105
- member_title_level = title_level + "#"
106
- for qual_name, name, item in module_items(module, filter_items):
107
- try:
108
- if hasattr(item, "__wrapped__"):
109
- item = item.__wrapped__
110
- except KeyError:
111
- pass
112
- except:
113
- print("failed on", qual_name, name, item)
114
- raise
115
- if inspect.isclass(item):
116
- classdoc = class_str(name, item, title_level=member_title_level)
117
- object_docs.append(f"{member_title_level} {qual_name}\n\n")
118
- object_docs.append(classdoc)
119
- elif callable(item):
120
- funcdoc = function_str(name, item)
121
- object_docs.append(f"{member_title_level} {qual_name}\n\n")
122
- object_docs.append(funcdoc)
123
- else:
124
- item_doc = getattr(module, f"__doc__{name}", None)
125
- if item_doc:
126
- # variable documentation
127
- object_docs.append(f"{member_title_level} {qual_name}\n\n")
128
- object_docs.append(item_doc)
129
- else:
130
- warnings.warn(f"Not sure how to document: {name} ({item}")
131
-
132
- if object_docs:
133
- return "".join(header + object_docs)
134
- return ""
135
-
136
-
137
- def object_is_private(name, obj):
138
- docstring = inspect.getdoc(obj)
139
- if docstring is None:
140
- docstring = ""
141
- module = getattr(obj, "__module__", None) # obj is class
142
- if not module:
143
- cls = getattr(obj, "__class__", None) # obj is instance
144
- if cls:
145
- module = getattr(cls, "__module__", None)
146
- if module == "builtins":
147
- return True
148
-
149
- if docstring.lstrip().startswith("mdmd:hidden") or name.startswith("_"):
150
- return True
151
-
152
- return False
153
-
154
-
155
- def default_filter(module, item_name):
156
- """Include non-private objects defined in the module itself"""
157
- item = getattr(module, item_name)
158
- if object_is_private(item_name, item) or inspect.ismodule(item):
159
- return False
160
- member_module = getattr(item, "__module__", type(item).__module__)
161
- return member_module == module.__name__
162
-
163
-
164
- def package_filter(module_prefix: str):
165
- """Include non-private objects defined in any module with the prefix `module_prefix`"""
166
-
167
- def return_filter(module, item_name):
168
- item = getattr(module, item_name)
169
- if object_is_private(item_name, item) or inspect.ismodule(item):
170
- return False
171
- member_module = getattr(item, "__module__", type(item).__module__)
172
- return member_module.startswith(module_prefix)
173
-
174
- return return_filter
175
-
176
-
177
- def module_items(module, filter_items: Callable[[ModuleType, str], bool] = None):
178
- """Returns filtered members of module"""
179
- if filter_items is None:
180
- # default filter is to only include classes and functions declared (or whose type is declared) in the file
181
- filter_items = default_filter
182
-
183
- for member_name, member in inspect.getmembers(module):
184
- # only modal items
185
- if not filter_items(module, member_name):
186
- continue
187
-
188
- qual_name = f"{module.__name__}.{member_name}"
189
- yield qual_name, member_name, member
190
-
191
-
192
- class Category(Enum):
193
- FUNCTION = "function"
194
- CLASS = "class"
195
- MODULE = "module"
@@ -1,76 +0,0 @@
1
- # Copyright Modal Labs 2023
2
- import ast
3
- import inspect
4
- import re
5
- import textwrap
6
- import warnings
7
-
8
- from synchronicity.synchronizer import FunctionWithAio
9
-
10
-
11
- def _signature_from_ast(func) -> tuple[str, str]:
12
- """Get function signature, including decorators and comments, from source code
13
-
14
- Traverses functools.wraps-wrappings to get source of underlying function.
15
-
16
- Has the advantage over inspect.signature that it can get decorators, default arguments and comments verbatim
17
- from the function definition.
18
- """
19
- src = inspect.getsource(func)
20
- src = textwrap.dedent(src)
21
-
22
- def get_source_segment(src, fromline, fromcol, toline, tocol) -> str:
23
- lines = src.split("\n")
24
- lines = lines[fromline - 1 : toline]
25
- lines[-1] = lines[-1][:tocol]
26
- lines[0] = lines[0][fromcol:]
27
- return "\n".join(lines)
28
-
29
- tree = ast.parse(src)
30
- func_def = list(ast.iter_child_nodes(tree))[0]
31
- assert isinstance(func_def, (ast.FunctionDef, ast.AsyncFunctionDef))
32
- decorator_starts = [(item.lineno, item.col_offset - 1) for item in func_def.decorator_list]
33
- declaration_start = min([(func_def.lineno, func_def.col_offset)] + decorator_starts)
34
- body_start = min((item.lineno, item.col_offset) for item in func_def.body)
35
-
36
- return (
37
- func_def.name,
38
- get_source_segment(src, declaration_start[0], declaration_start[1], body_start[0], body_start[1] - 1).strip(),
39
- )
40
-
41
-
42
- def get_signature(name, callable) -> str:
43
- """A problem with using *only* this method is that the wrapping method signature will not be respected.
44
- TODO: use source parsing *only* to extract default arguments, comments (and possibly decorators) and "merge"
45
- that definition with the outer-most definition."""
46
-
47
- if not (inspect.isfunction(callable) or inspect.ismethod(callable) or isinstance(callable, FunctionWithAio)):
48
- assert hasattr(callable, "__call__")
49
- callable = callable.__call__
50
-
51
- try:
52
- original_name, definition_source = _signature_from_ast(callable)
53
- except Exception:
54
- warnings.warn(f"Could not get source signature for {name}. Using fallback.")
55
- original_name = name
56
- definition_source = f"def {name}{inspect.signature(callable)}"
57
-
58
- if original_name != name:
59
- # ugly name and definition replacement hack when needed
60
- definition_source = definition_source.replace(f"def {original_name}", f"def {name}")
61
-
62
- if (
63
- "async def" in definition_source
64
- and not inspect.iscoroutinefunction(callable)
65
- and not inspect.isasyncgenfunction(callable)
66
- ):
67
- # hack to "reset" signature to a blocking one if the underlying source definition is async
68
- # but the wrapper function isn't (like when synchronicity wraps an async function as a blocking one)
69
- definition_source = definition_source.replace("async def", "def")
70
- definition_source = definition_source.replace("asynccontextmanager", "contextmanager")
71
- definition_source = definition_source.replace("AsyncIterator", "Iterator")
72
-
73
- # remove any synchronicity-internal decorators
74
- definition_source, _ = re.subn(r"^\s*@synchronizer\..*\n", "", definition_source)
75
-
76
- return definition_source
@@ -1 +0,0 @@
1
- # Copyright Modal Labs 2022
@@ -1 +0,0 @@
1
- # Copyright Modal Labs 2024
@@ -1,28 +0,0 @@
1
- # Copyright Modal Labs 2022
2
- import os
3
- import sys
4
- from typing import cast
5
-
6
- import modal
7
- from modal.image import SUPPORTED_PYTHON_SERIES, ImageBuilderVersion
8
-
9
-
10
- def dummy():
11
- pass
12
-
13
-
14
- if __name__ == "__main__":
15
- _, name = sys.argv
16
- constructor = getattr(modal.Image, name)
17
-
18
- builder_version = os.environ.get("MODAL_IMAGE_BUILDER_VERSION")
19
- assert builder_version, "Script requires MODAL_IMAGE_BUILDER_VERSION environment variable"
20
- python_versions = SUPPORTED_PYTHON_SERIES[cast(ImageBuilderVersion, builder_version)]
21
-
22
- app = modal.App(f"build-{name.replace('_', '-')}-image")
23
- for v in python_versions:
24
- app.function(image=constructor(python_version=v), name=f"{v}")(dummy)
25
-
26
- with modal.enable_output():
27
- with app.run():
28
- pass
@@ -1 +0,0 @@
1
- # Copyright Modal Labs 2024
@@ -1,29 +0,0 @@
1
- # Copyright Modal Labs 2022
2
- from modal.config import config
3
- from modal.mount import (
4
- client_mount_name,
5
- create_client_mount,
6
- )
7
- from modal_proto import api_pb2
8
-
9
-
10
- def publish_client_mount(client):
11
- mount = create_client_mount()
12
- name = client_mount_name()
13
- profile_environment = config.get("environment")
14
- # TODO: change how namespaces work, so we don't have to use unrelated workspaces when deploying to global?.
15
- mount._deploy(
16
- client_mount_name(),
17
- api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL,
18
- client=client,
19
- environment_name=profile_environment,
20
- )
21
- print(f"✅ Deployed client mount {name} to global namespace.")
22
-
23
-
24
- def main(client=None):
25
- publish_client_mount(client)
26
-
27
-
28
- if __name__ == "__main__":
29
- main()
@@ -1,53 +0,0 @@
1
- # Copyright Modal Labs 2022
2
- import shutil
3
- import tempfile
4
- import urllib.request
5
-
6
- from modal.config import config
7
- from modal.exception import NotFoundError
8
- from modal.mount import (
9
- PYTHON_STANDALONE_VERSIONS,
10
- Mount,
11
- python_standalone_mount_name,
12
- )
13
- from modal_proto import api_pb2
14
-
15
-
16
- def publish_python_standalone_mount(client, version: str) -> None:
17
- release, full_version = PYTHON_STANDALONE_VERSIONS[version]
18
-
19
- libc = "gnu"
20
- arch = "x86_64_v3"
21
- url = (
22
- "https://github.com/indygreg/python-build-standalone/releases/download"
23
- + f"/{release}/cpython-{full_version}+{release}-{arch}-unknown-linux-gnu-install_only.tar.gz"
24
- )
25
-
26
- profile_environment = config.get("environment")
27
- mount_name = python_standalone_mount_name(f"{version}-{libc}")
28
- try:
29
- Mount.from_name(mount_name, namespace=api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL).hydrate(client)
30
- print(f"✅ Found existing mount {mount_name} in global namespace.")
31
- except NotFoundError:
32
- print(f"📦 Unpacking python-build-standalone for {version}-{libc}.")
33
- with tempfile.TemporaryDirectory() as d:
34
- urllib.request.urlretrieve(url, f"{d}/cpython.tar.gz")
35
- shutil.unpack_archive(f"{d}/cpython.tar.gz", d)
36
- print(f"🌐 Downloaded and unpacked archive to {d}.")
37
- python_mount = Mount._from_local_dir(f"{d}/python")
38
- python_mount._deploy(
39
- mount_name,
40
- api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL,
41
- client=client,
42
- environment_name=profile_environment,
43
- )
44
- print(f"✅ Deployed mount {mount_name} to global namespace.")
45
-
46
-
47
- def main(client=None):
48
- for version in PYTHON_STANDALONE_VERSIONS:
49
- publish_python_standalone_mount(client, version)
50
-
51
-
52
- if __name__ == "__main__":
53
- main()