modal 0.62.16__py3-none-any.whl → 0.72.11__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.
Files changed (220) hide show
  1. modal/__init__.py +17 -13
  2. modal/__main__.py +41 -3
  3. modal/_clustered_functions.py +80 -0
  4. modal/_clustered_functions.pyi +22 -0
  5. modal/_container_entrypoint.py +420 -937
  6. modal/_ipython.py +3 -13
  7. modal/_location.py +17 -10
  8. modal/_output.py +243 -99
  9. modal/_pty.py +2 -2
  10. modal/_resolver.py +55 -59
  11. modal/_resources.py +51 -0
  12. modal/_runtime/__init__.py +1 -0
  13. modal/_runtime/asgi.py +519 -0
  14. modal/_runtime/container_io_manager.py +1036 -0
  15. modal/_runtime/execution_context.py +89 -0
  16. modal/_runtime/telemetry.py +169 -0
  17. modal/_runtime/user_code_imports.py +356 -0
  18. modal/_serialization.py +134 -9
  19. modal/_traceback.py +47 -187
  20. modal/_tunnel.py +52 -16
  21. modal/_tunnel.pyi +19 -36
  22. modal/_utils/app_utils.py +3 -17
  23. modal/_utils/async_utils.py +479 -100
  24. modal/_utils/blob_utils.py +157 -186
  25. modal/_utils/bytes_io_segment_payload.py +97 -0
  26. modal/_utils/deprecation.py +89 -0
  27. modal/_utils/docker_utils.py +98 -0
  28. modal/_utils/function_utils.py +460 -171
  29. modal/_utils/grpc_testing.py +47 -31
  30. modal/_utils/grpc_utils.py +62 -109
  31. modal/_utils/hash_utils.py +61 -19
  32. modal/_utils/http_utils.py +39 -9
  33. modal/_utils/logger.py +2 -1
  34. modal/_utils/mount_utils.py +34 -16
  35. modal/_utils/name_utils.py +58 -0
  36. modal/_utils/package_utils.py +14 -1
  37. modal/_utils/pattern_utils.py +205 -0
  38. modal/_utils/rand_pb_testing.py +5 -7
  39. modal/_utils/shell_utils.py +15 -49
  40. modal/_vendor/a2wsgi_wsgi.py +62 -72
  41. modal/_vendor/cloudpickle.py +1 -1
  42. modal/_watcher.py +14 -12
  43. modal/app.py +1003 -314
  44. modal/app.pyi +540 -264
  45. modal/call_graph.py +7 -6
  46. modal/cli/_download.py +63 -53
  47. modal/cli/_traceback.py +200 -0
  48. modal/cli/app.py +205 -45
  49. modal/cli/config.py +12 -5
  50. modal/cli/container.py +62 -14
  51. modal/cli/dict.py +128 -0
  52. modal/cli/entry_point.py +26 -13
  53. modal/cli/environment.py +40 -9
  54. modal/cli/import_refs.py +64 -58
  55. modal/cli/launch.py +32 -18
  56. modal/cli/network_file_system.py +64 -83
  57. modal/cli/profile.py +1 -1
  58. modal/cli/programs/run_jupyter.py +35 -10
  59. modal/cli/programs/vscode.py +60 -10
  60. modal/cli/queues.py +131 -0
  61. modal/cli/run.py +234 -131
  62. modal/cli/secret.py +8 -7
  63. modal/cli/token.py +7 -2
  64. modal/cli/utils.py +79 -10
  65. modal/cli/volume.py +110 -109
  66. modal/client.py +250 -144
  67. modal/client.pyi +157 -118
  68. modal/cloud_bucket_mount.py +108 -34
  69. modal/cloud_bucket_mount.pyi +32 -38
  70. modal/cls.py +535 -148
  71. modal/cls.pyi +190 -146
  72. modal/config.py +41 -19
  73. modal/container_process.py +177 -0
  74. modal/container_process.pyi +82 -0
  75. modal/dict.py +111 -65
  76. modal/dict.pyi +136 -131
  77. modal/environments.py +106 -5
  78. modal/environments.pyi +77 -25
  79. modal/exception.py +34 -43
  80. modal/experimental.py +61 -2
  81. modal/extensions/ipython.py +5 -5
  82. modal/file_io.py +537 -0
  83. modal/file_io.pyi +235 -0
  84. modal/file_pattern_matcher.py +197 -0
  85. modal/functions.py +906 -911
  86. modal/functions.pyi +466 -430
  87. modal/gpu.py +57 -44
  88. modal/image.py +1089 -479
  89. modal/image.pyi +584 -228
  90. modal/io_streams.py +434 -0
  91. modal/io_streams.pyi +122 -0
  92. modal/mount.py +314 -101
  93. modal/mount.pyi +241 -235
  94. modal/network_file_system.py +92 -92
  95. modal/network_file_system.pyi +152 -110
  96. modal/object.py +67 -36
  97. modal/object.pyi +166 -143
  98. modal/output.py +63 -0
  99. modal/parallel_map.py +434 -0
  100. modal/parallel_map.pyi +75 -0
  101. modal/partial_function.py +282 -117
  102. modal/partial_function.pyi +222 -129
  103. modal/proxy.py +15 -12
  104. modal/proxy.pyi +3 -8
  105. modal/queue.py +182 -65
  106. modal/queue.pyi +218 -118
  107. modal/requirements/2024.04.txt +29 -0
  108. modal/requirements/2024.10.txt +16 -0
  109. modal/requirements/README.md +21 -0
  110. modal/requirements/base-images.json +22 -0
  111. modal/retries.py +48 -7
  112. modal/runner.py +459 -156
  113. modal/runner.pyi +135 -71
  114. modal/running_app.py +38 -0
  115. modal/sandbox.py +514 -236
  116. modal/sandbox.pyi +397 -169
  117. modal/schedule.py +4 -4
  118. modal/scheduler_placement.py +20 -3
  119. modal/secret.py +56 -31
  120. modal/secret.pyi +62 -42
  121. modal/serving.py +51 -56
  122. modal/serving.pyi +44 -36
  123. modal/stream_type.py +15 -0
  124. modal/token_flow.py +5 -3
  125. modal/token_flow.pyi +37 -32
  126. modal/volume.py +285 -157
  127. modal/volume.pyi +249 -184
  128. {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/METADATA +7 -7
  129. modal-0.72.11.dist-info/RECORD +174 -0
  130. {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/top_level.txt +0 -1
  131. modal_docs/gen_reference_docs.py +3 -1
  132. modal_docs/mdmd/mdmd.py +0 -1
  133. modal_docs/mdmd/signatures.py +5 -2
  134. modal_global_objects/images/base_images.py +28 -0
  135. modal_global_objects/mounts/python_standalone.py +2 -2
  136. modal_proto/__init__.py +1 -1
  137. modal_proto/api.proto +1288 -533
  138. modal_proto/api_grpc.py +856 -456
  139. modal_proto/api_pb2.py +2165 -1157
  140. modal_proto/api_pb2.pyi +8859 -0
  141. modal_proto/api_pb2_grpc.py +1674 -855
  142. modal_proto/api_pb2_grpc.pyi +1416 -0
  143. modal_proto/modal_api_grpc.py +149 -0
  144. modal_proto/modal_options_grpc.py +3 -0
  145. modal_proto/options_pb2.pyi +20 -0
  146. modal_proto/options_pb2_grpc.pyi +7 -0
  147. modal_proto/py.typed +0 -0
  148. modal_version/__init__.py +1 -1
  149. modal_version/_version_generated.py +2 -2
  150. modal/_asgi.py +0 -370
  151. modal/_container_entrypoint.pyi +0 -378
  152. modal/_container_exec.py +0 -128
  153. modal/_sandbox_shell.py +0 -49
  154. modal/shared_volume.py +0 -23
  155. modal/shared_volume.pyi +0 -24
  156. modal/stub.py +0 -783
  157. modal/stub.pyi +0 -332
  158. modal-0.62.16.dist-info/RECORD +0 -198
  159. modal_global_objects/images/conda.py +0 -15
  160. modal_global_objects/images/debian_slim.py +0 -15
  161. modal_global_objects/images/micromamba.py +0 -15
  162. test/__init__.py +0 -1
  163. test/aio_test.py +0 -12
  164. test/async_utils_test.py +0 -262
  165. test/blob_test.py +0 -67
  166. test/cli_imports_test.py +0 -149
  167. test/cli_test.py +0 -659
  168. test/client_test.py +0 -194
  169. test/cls_test.py +0 -630
  170. test/config_test.py +0 -137
  171. test/conftest.py +0 -1420
  172. test/container_app_test.py +0 -32
  173. test/container_test.py +0 -1389
  174. test/cpu_test.py +0 -23
  175. test/decorator_test.py +0 -85
  176. test/deprecation_test.py +0 -34
  177. test/dict_test.py +0 -33
  178. test/e2e_test.py +0 -68
  179. test/error_test.py +0 -7
  180. test/function_serialization_test.py +0 -32
  181. test/function_test.py +0 -653
  182. test/function_utils_test.py +0 -101
  183. test/gpu_test.py +0 -159
  184. test/grpc_utils_test.py +0 -141
  185. test/helpers.py +0 -42
  186. test/image_test.py +0 -669
  187. test/live_reload_test.py +0 -80
  188. test/lookup_test.py +0 -70
  189. test/mdmd_test.py +0 -329
  190. test/mount_test.py +0 -162
  191. test/mounted_files_test.py +0 -329
  192. test/network_file_system_test.py +0 -181
  193. test/notebook_test.py +0 -66
  194. test/object_test.py +0 -41
  195. test/package_utils_test.py +0 -25
  196. test/queue_test.py +0 -97
  197. test/resolver_test.py +0 -58
  198. test/retries_test.py +0 -67
  199. test/runner_test.py +0 -85
  200. test/sandbox_test.py +0 -191
  201. test/schedule_test.py +0 -15
  202. test/scheduler_placement_test.py +0 -29
  203. test/secret_test.py +0 -78
  204. test/serialization_test.py +0 -42
  205. test/stub_composition_test.py +0 -10
  206. test/stub_test.py +0 -360
  207. test/test_asgi_wrapper.py +0 -234
  208. test/token_flow_test.py +0 -18
  209. test/traceback_test.py +0 -135
  210. test/tunnel_test.py +0 -29
  211. test/utils_test.py +0 -88
  212. test/version_test.py +0 -14
  213. test/volume_test.py +0 -341
  214. test/watcher_test.py +0 -30
  215. test/webhook_test.py +0 -146
  216. /modal/{requirements.312.txt → requirements/2023.12.312.txt} +0 -0
  217. /modal/{requirements.txt → requirements/2023.12.txt} +0 -0
  218. {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/LICENSE +0 -0
  219. {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/WHEEL +0 -0
  220. {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/entry_points.txt +0 -0
modal/sandbox.pyi CHANGED
@@ -1,217 +1,445 @@
1
+ import _typeshed
2
+ import collections.abc
1
3
  import google.protobuf.message
4
+ import modal._tunnel
5
+ import modal.app
2
6
  import modal.client
3
7
  import modal.cloud_bucket_mount
8
+ import modal.container_process
9
+ import modal.file_io
4
10
  import modal.gpu
5
11
  import modal.image
12
+ import modal.io_streams
6
13
  import modal.mount
7
14
  import modal.network_file_system
8
15
  import modal.object
16
+ import modal.proxy
17
+ import modal.scheduler_placement
9
18
  import modal.secret
19
+ import modal.stream_type
10
20
  import modal.volume
11
21
  import modal_proto.api_pb2
12
22
  import os
13
23
  import typing
14
24
  import typing_extensions
15
25
 
16
- class _LogsReader:
17
- def __init__(self, file_descriptor: int, sandbox_id: str, client: modal.client._Client) -> None:
18
- ...
19
-
20
- async def read(self) -> str:
21
- ...
22
-
23
- def _get_logs(self) -> typing.AsyncIterator[typing.Union[modal_proto.api_pb2.TaskLogs, None]]:
24
- ...
25
-
26
- def __aiter__(self):
27
- ...
28
-
29
- async def __anext__(self):
30
- ...
31
-
32
-
33
- class _StreamWriter:
34
- def __init__(self, sandbox_id: str, client: modal.client._Client):
35
- ...
36
-
37
- def get_next_index(self):
38
- ...
39
-
40
- def write(self, data: typing.Union[bytes, bytearray, memoryview]):
41
- ...
42
-
43
- def write_eof(self):
44
- ...
45
-
46
- async def drain(self):
47
- ...
48
-
49
-
50
- class LogsReader:
51
- def __init__(self, file_descriptor: int, sandbox_id: str, client: modal.client.Client) -> None:
52
- ...
53
-
54
- class __read_spec(typing_extensions.Protocol):
55
- def __call__(self) -> str:
56
- ...
57
-
58
- async def aio(self, *args, **kwargs) -> str:
59
- ...
60
-
61
- read: __read_spec
62
-
63
- class ___get_logs_spec(typing_extensions.Protocol):
64
- def __call__(self) -> typing.Iterator[typing.Union[modal_proto.api_pb2.TaskLogs, None]]:
65
- ...
66
-
67
- def aio(self) -> typing.AsyncIterator[typing.Union[modal_proto.api_pb2.TaskLogs, None]]:
68
- ...
69
-
70
- _get_logs: ___get_logs_spec
71
-
72
- def __iter__(self):
73
- ...
74
-
75
- def __aiter__(self):
76
- ...
77
-
78
- def __next__(self):
79
- ...
80
-
81
- async def __anext__(self, *args, **kwargs):
82
- ...
83
-
84
-
85
- class StreamWriter:
86
- def __init__(self, sandbox_id: str, client: modal.client.Client):
87
- ...
88
-
89
- def get_next_index(self):
90
- ...
91
-
92
- def write(self, data: typing.Union[bytes, bytearray, memoryview]):
93
- ...
94
-
95
- def write_eof(self):
96
- ...
97
-
98
- class __drain_spec(typing_extensions.Protocol):
99
- def __call__(self):
100
- ...
101
-
102
- async def aio(self, *args, **kwargs):
103
- ...
104
-
105
- drain: __drain_spec
106
-
107
-
108
26
  class _Sandbox(modal.object._Object):
109
- _result: typing.Union[modal_proto.api_pb2.GenericResult, None]
110
- _stdout: _LogsReader
111
- _stderr: _LogsReader
112
- _stdin: _StreamWriter
27
+ _result: typing.Optional[modal_proto.api_pb2.GenericResult]
28
+ _stdout: modal.io_streams._StreamReader[str]
29
+ _stderr: modal.io_streams._StreamReader[str]
30
+ _stdin: modal.io_streams._StreamWriter
31
+ _task_id: typing.Optional[str]
32
+ _tunnels: typing.Optional[dict[int, modal._tunnel.Tunnel]]
113
33
 
114
34
  @staticmethod
115
- def _new(entrypoint_args: typing.Sequence[str], image: modal.image._Image, mounts: typing.Sequence[modal.mount._Mount], secrets: typing.Sequence[modal.secret._Secret], timeout: typing.Union[int, None] = None, workdir: typing.Union[str, None] = None, gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None, cloud: typing.Union[str, None] = None, cpu: typing.Union[float, None] = None, memory: typing.Union[int, None] = None, network_file_systems: typing.Dict[typing.Union[str, os.PathLike], modal.network_file_system._NetworkFileSystem] = {}, block_network: bool = False, volumes: typing.Dict[typing.Union[str, os.PathLike], typing.Union[modal.volume._Volume, modal.cloud_bucket_mount._CloudBucketMount]] = {}, allow_background_volume_commits: bool = False, pty_info: typing.Union[modal_proto.api_pb2.PTYInfo, None] = None) -> _Sandbox:
116
- ...
117
-
118
- def _hydrate_metadata(self, handle_metadata: typing.Union[google.protobuf.message.Message, None]):
119
- ...
120
-
35
+ def _new(
36
+ entrypoint_args: collections.abc.Sequence[str],
37
+ image: modal.image._Image,
38
+ mounts: collections.abc.Sequence[modal.mount._Mount],
39
+ secrets: collections.abc.Sequence[modal.secret._Secret],
40
+ timeout: typing.Optional[int] = None,
41
+ workdir: typing.Optional[str] = None,
42
+ gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
43
+ cloud: typing.Optional[str] = None,
44
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
45
+ cpu: typing.Optional[float] = None,
46
+ memory: typing.Union[int, tuple[int, int], None] = None,
47
+ network_file_systems: dict[typing.Union[str, os.PathLike], modal.network_file_system._NetworkFileSystem] = {},
48
+ block_network: bool = False,
49
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
50
+ volumes: dict[
51
+ typing.Union[str, os.PathLike],
52
+ typing.Union[modal.volume._Volume, modal.cloud_bucket_mount._CloudBucketMount],
53
+ ] = {},
54
+ pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
55
+ encrypted_ports: collections.abc.Sequence[int] = [],
56
+ unencrypted_ports: collections.abc.Sequence[int] = [],
57
+ proxy: typing.Optional[modal.proxy._Proxy] = None,
58
+ _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
59
+ ) -> _Sandbox: ...
121
60
  @staticmethod
122
- async def from_id(sandbox_id: str, client: typing.Union[modal.client._Client, None] = None) -> _Sandbox:
123
- ...
124
-
125
- async def wait(self, raise_on_termination: bool = True):
126
- ...
127
-
128
- async def terminate(self):
129
- ...
130
-
131
- async def poll(self) -> typing.Union[int, None]:
132
- ...
133
-
61
+ async def create(
62
+ *entrypoint_args: str,
63
+ app: typing.Optional[modal.app._App] = None,
64
+ environment_name: typing.Optional[str] = None,
65
+ image: typing.Optional[modal.image._Image] = None,
66
+ mounts: collections.abc.Sequence[modal.mount._Mount] = (),
67
+ secrets: collections.abc.Sequence[modal.secret._Secret] = (),
68
+ network_file_systems: dict[typing.Union[str, os.PathLike], modal.network_file_system._NetworkFileSystem] = {},
69
+ timeout: typing.Optional[int] = None,
70
+ workdir: typing.Optional[str] = None,
71
+ gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
72
+ cloud: typing.Optional[str] = None,
73
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
74
+ cpu: typing.Union[float, tuple[float, float], None] = None,
75
+ memory: typing.Union[int, tuple[int, int], None] = None,
76
+ block_network: bool = False,
77
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
78
+ volumes: dict[
79
+ typing.Union[str, os.PathLike],
80
+ typing.Union[modal.volume._Volume, modal.cloud_bucket_mount._CloudBucketMount],
81
+ ] = {},
82
+ pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
83
+ encrypted_ports: collections.abc.Sequence[int] = [],
84
+ unencrypted_ports: collections.abc.Sequence[int] = [],
85
+ proxy: typing.Optional[modal.proxy._Proxy] = None,
86
+ _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
87
+ client: typing.Optional[modal.client._Client] = None,
88
+ ) -> _Sandbox: ...
89
+ def _hydrate_metadata(self, handle_metadata: typing.Optional[google.protobuf.message.Message]): ...
90
+ @staticmethod
91
+ async def from_id(sandbox_id: str, client: typing.Optional[modal.client._Client] = None) -> _Sandbox: ...
92
+ async def set_tags(self, tags: dict[str, str], *, client: typing.Optional[modal.client._Client] = None): ...
93
+ async def snapshot_filesystem(self, timeout: int = 55) -> modal.image._Image: ...
94
+ async def wait(self, raise_on_termination: bool = True): ...
95
+ async def tunnels(self, timeout: int = 50) -> dict[int, modal._tunnel.Tunnel]: ...
96
+ async def terminate(self): ...
97
+ async def poll(self) -> typing.Optional[int]: ...
98
+ async def _get_task_id(self): ...
99
+ @typing.overload
100
+ async def exec(
101
+ self,
102
+ *cmds: str,
103
+ pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
104
+ stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
105
+ stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
106
+ timeout: typing.Optional[int] = None,
107
+ workdir: typing.Optional[str] = None,
108
+ secrets: collections.abc.Sequence[modal.secret._Secret] = (),
109
+ text: typing.Literal[True] = True,
110
+ bufsize: typing.Literal[-1, 1] = -1,
111
+ _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
112
+ ) -> modal.container_process._ContainerProcess[str]: ...
113
+ @typing.overload
114
+ async def exec(
115
+ self,
116
+ *cmds: str,
117
+ pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
118
+ stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
119
+ stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
120
+ timeout: typing.Optional[int] = None,
121
+ workdir: typing.Optional[str] = None,
122
+ secrets: collections.abc.Sequence[modal.secret._Secret] = (),
123
+ text: typing.Literal[False] = False,
124
+ bufsize: typing.Literal[-1, 1] = -1,
125
+ _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
126
+ ) -> modal.container_process._ContainerProcess[bytes]: ...
127
+ @typing.overload
128
+ async def open(self, path: str, mode: _typeshed.OpenTextMode) -> modal.file_io._FileIO[str]: ...
129
+ @typing.overload
130
+ async def open(self, path: str, mode: _typeshed.OpenBinaryMode) -> modal.file_io._FileIO[bytes]: ...
131
+ async def ls(self, path: str) -> list[str]: ...
132
+ async def mkdir(self, path: str, parents: bool = False) -> None: ...
133
+ async def rm(self, path: str, recursive: bool = False) -> None: ...
134
+ def watch(
135
+ self,
136
+ path: str,
137
+ filter: typing.Optional[list[modal.file_io.FileWatchEventType]] = None,
138
+ recursive: typing.Optional[bool] = None,
139
+ timeout: typing.Optional[int] = None,
140
+ ) -> typing.AsyncIterator[modal.file_io.FileWatchEvent]: ...
134
141
  @property
135
- def stdout(self) -> _LogsReader:
136
- ...
137
-
142
+ def stdout(self) -> modal.io_streams._StreamReader[str]: ...
138
143
  @property
139
- def stderr(self) -> _LogsReader:
140
- ...
141
-
144
+ def stderr(self) -> modal.io_streams._StreamReader[str]: ...
142
145
  @property
143
- def stdin(self) -> _StreamWriter:
144
- ...
145
-
146
+ def stdin(self) -> modal.io_streams._StreamWriter: ...
146
147
  @property
147
- def returncode(self) -> typing.Union[int, None]:
148
- ...
149
-
148
+ def returncode(self) -> typing.Optional[int]: ...
149
+ @staticmethod
150
+ def list(
151
+ *,
152
+ app_id: typing.Optional[str] = None,
153
+ tags: typing.Optional[dict[str, str]] = None,
154
+ client: typing.Optional[modal.client._Client] = None,
155
+ ) -> collections.abc.AsyncGenerator[_Sandbox, None]: ...
150
156
 
151
157
  class Sandbox(modal.object.Object):
152
- _result: typing.Union[modal_proto.api_pb2.GenericResult, None]
153
- _stdout: LogsReader
154
- _stderr: LogsReader
155
- _stdin: StreamWriter
158
+ _result: typing.Optional[modal_proto.api_pb2.GenericResult]
159
+ _stdout: modal.io_streams.StreamReader[str]
160
+ _stderr: modal.io_streams.StreamReader[str]
161
+ _stdin: modal.io_streams.StreamWriter
162
+ _task_id: typing.Optional[str]
163
+ _tunnels: typing.Optional[dict[int, modal._tunnel.Tunnel]]
164
+
165
+ def __init__(self, *args, **kwargs): ...
166
+ @staticmethod
167
+ def _new(
168
+ entrypoint_args: collections.abc.Sequence[str],
169
+ image: modal.image.Image,
170
+ mounts: collections.abc.Sequence[modal.mount.Mount],
171
+ secrets: collections.abc.Sequence[modal.secret.Secret],
172
+ timeout: typing.Optional[int] = None,
173
+ workdir: typing.Optional[str] = None,
174
+ gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
175
+ cloud: typing.Optional[str] = None,
176
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
177
+ cpu: typing.Optional[float] = None,
178
+ memory: typing.Union[int, tuple[int, int], None] = None,
179
+ network_file_systems: dict[typing.Union[str, os.PathLike], modal.network_file_system.NetworkFileSystem] = {},
180
+ block_network: bool = False,
181
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
182
+ volumes: dict[
183
+ typing.Union[str, os.PathLike], typing.Union[modal.volume.Volume, modal.cloud_bucket_mount.CloudBucketMount]
184
+ ] = {},
185
+ pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
186
+ encrypted_ports: collections.abc.Sequence[int] = [],
187
+ unencrypted_ports: collections.abc.Sequence[int] = [],
188
+ proxy: typing.Optional[modal.proxy.Proxy] = None,
189
+ _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
190
+ ) -> Sandbox: ...
191
+
192
+ class __create_spec(typing_extensions.Protocol):
193
+ def __call__(
194
+ self,
195
+ *entrypoint_args: str,
196
+ app: typing.Optional[modal.app.App] = None,
197
+ environment_name: typing.Optional[str] = None,
198
+ image: typing.Optional[modal.image.Image] = None,
199
+ mounts: collections.abc.Sequence[modal.mount.Mount] = (),
200
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
201
+ network_file_systems: dict[
202
+ typing.Union[str, os.PathLike], modal.network_file_system.NetworkFileSystem
203
+ ] = {},
204
+ timeout: typing.Optional[int] = None,
205
+ workdir: typing.Optional[str] = None,
206
+ gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
207
+ cloud: typing.Optional[str] = None,
208
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
209
+ cpu: typing.Union[float, tuple[float, float], None] = None,
210
+ memory: typing.Union[int, tuple[int, int], None] = None,
211
+ block_network: bool = False,
212
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
213
+ volumes: dict[
214
+ typing.Union[str, os.PathLike],
215
+ typing.Union[modal.volume.Volume, modal.cloud_bucket_mount.CloudBucketMount],
216
+ ] = {},
217
+ pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
218
+ encrypted_ports: collections.abc.Sequence[int] = [],
219
+ unencrypted_ports: collections.abc.Sequence[int] = [],
220
+ proxy: typing.Optional[modal.proxy.Proxy] = None,
221
+ _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
222
+ client: typing.Optional[modal.client.Client] = None,
223
+ ) -> Sandbox: ...
224
+ async def aio(
225
+ self,
226
+ *entrypoint_args: str,
227
+ app: typing.Optional[modal.app.App] = None,
228
+ environment_name: typing.Optional[str] = None,
229
+ image: typing.Optional[modal.image.Image] = None,
230
+ mounts: collections.abc.Sequence[modal.mount.Mount] = (),
231
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
232
+ network_file_systems: dict[
233
+ typing.Union[str, os.PathLike], modal.network_file_system.NetworkFileSystem
234
+ ] = {},
235
+ timeout: typing.Optional[int] = None,
236
+ workdir: typing.Optional[str] = None,
237
+ gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
238
+ cloud: typing.Optional[str] = None,
239
+ region: typing.Union[str, collections.abc.Sequence[str], None] = None,
240
+ cpu: typing.Union[float, tuple[float, float], None] = None,
241
+ memory: typing.Union[int, tuple[int, int], None] = None,
242
+ block_network: bool = False,
243
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
244
+ volumes: dict[
245
+ typing.Union[str, os.PathLike],
246
+ typing.Union[modal.volume.Volume, modal.cloud_bucket_mount.CloudBucketMount],
247
+ ] = {},
248
+ pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
249
+ encrypted_ports: collections.abc.Sequence[int] = [],
250
+ unencrypted_ports: collections.abc.Sequence[int] = [],
251
+ proxy: typing.Optional[modal.proxy.Proxy] = None,
252
+ _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
253
+ client: typing.Optional[modal.client.Client] = None,
254
+ ) -> Sandbox: ...
255
+
256
+ create: __create_spec
257
+
258
+ def _hydrate_metadata(self, handle_metadata: typing.Optional[google.protobuf.message.Message]): ...
156
259
 
157
- def __init__(self, *args, **kwargs):
158
- ...
260
+ class __from_id_spec(typing_extensions.Protocol):
261
+ def __call__(self, sandbox_id: str, client: typing.Optional[modal.client.Client] = None) -> Sandbox: ...
262
+ async def aio(self, sandbox_id: str, client: typing.Optional[modal.client.Client] = None) -> Sandbox: ...
159
263
 
160
- @staticmethod
161
- def _new(entrypoint_args: typing.Sequence[str], image: modal.image.Image, mounts: typing.Sequence[modal.mount.Mount], secrets: typing.Sequence[modal.secret.Secret], timeout: typing.Union[int, None] = None, workdir: typing.Union[str, None] = None, gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None, cloud: typing.Union[str, None] = None, cpu: typing.Union[float, None] = None, memory: typing.Union[int, None] = None, network_file_systems: typing.Dict[typing.Union[str, os.PathLike], modal.network_file_system.NetworkFileSystem] = {}, block_network: bool = False, volumes: typing.Dict[typing.Union[str, os.PathLike], typing.Union[modal.volume.Volume, modal.cloud_bucket_mount.CloudBucketMount]] = {}, allow_background_volume_commits: bool = False, pty_info: typing.Union[modal_proto.api_pb2.PTYInfo, None] = None) -> Sandbox:
162
- ...
264
+ from_id: __from_id_spec
163
265
 
164
- def _hydrate_metadata(self, handle_metadata: typing.Union[google.protobuf.message.Message, None]):
165
- ...
266
+ class __set_tags_spec(typing_extensions.Protocol):
267
+ def __call__(self, tags: dict[str, str], *, client: typing.Optional[modal.client.Client] = None): ...
268
+ async def aio(self, tags: dict[str, str], *, client: typing.Optional[modal.client.Client] = None): ...
166
269
 
167
- class __from_id_spec(typing_extensions.Protocol):
168
- def __call__(self, sandbox_id: str, client: typing.Union[modal.client.Client, None] = None) -> Sandbox:
169
- ...
270
+ set_tags: __set_tags_spec
170
271
 
171
- async def aio(self, *args, **kwargs) -> Sandbox:
172
- ...
272
+ class __snapshot_filesystem_spec(typing_extensions.Protocol):
273
+ def __call__(self, timeout: int = 55) -> modal.image.Image: ...
274
+ async def aio(self, timeout: int = 55) -> modal.image.Image: ...
173
275
 
174
- from_id: __from_id_spec
276
+ snapshot_filesystem: __snapshot_filesystem_spec
175
277
 
176
278
  class __wait_spec(typing_extensions.Protocol):
177
- def __call__(self, raise_on_termination: bool = True):
178
- ...
179
-
180
- async def aio(self, *args, **kwargs):
181
- ...
279
+ def __call__(self, raise_on_termination: bool = True): ...
280
+ async def aio(self, raise_on_termination: bool = True): ...
182
281
 
183
282
  wait: __wait_spec
184
283
 
185
- class __terminate_spec(typing_extensions.Protocol):
186
- def __call__(self):
187
- ...
284
+ class __tunnels_spec(typing_extensions.Protocol):
285
+ def __call__(self, timeout: int = 50) -> dict[int, modal._tunnel.Tunnel]: ...
286
+ async def aio(self, timeout: int = 50) -> dict[int, modal._tunnel.Tunnel]: ...
188
287
 
189
- async def aio(self, *args, **kwargs):
190
- ...
288
+ tunnels: __tunnels_spec
289
+
290
+ class __terminate_spec(typing_extensions.Protocol):
291
+ def __call__(self): ...
292
+ async def aio(self): ...
191
293
 
192
294
  terminate: __terminate_spec
193
295
 
194
296
  class __poll_spec(typing_extensions.Protocol):
195
- def __call__(self) -> typing.Union[int, None]:
196
- ...
197
-
198
- async def aio(self, *args, **kwargs) -> typing.Union[int, None]:
199
- ...
297
+ def __call__(self) -> typing.Optional[int]: ...
298
+ async def aio(self) -> typing.Optional[int]: ...
200
299
 
201
300
  poll: __poll_spec
202
301
 
203
- @property
204
- def stdout(self) -> LogsReader:
205
- ...
302
+ class ___get_task_id_spec(typing_extensions.Protocol):
303
+ def __call__(self): ...
304
+ async def aio(self): ...
305
+
306
+ _get_task_id: ___get_task_id_spec
307
+
308
+ class __exec_spec(typing_extensions.Protocol):
309
+ @typing.overload
310
+ def __call__(
311
+ self,
312
+ *cmds: str,
313
+ pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
314
+ stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
315
+ stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
316
+ timeout: typing.Optional[int] = None,
317
+ workdir: typing.Optional[str] = None,
318
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
319
+ text: typing.Literal[True] = True,
320
+ bufsize: typing.Literal[-1, 1] = -1,
321
+ _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
322
+ ) -> modal.container_process.ContainerProcess[str]: ...
323
+ @typing.overload
324
+ def __call__(
325
+ self,
326
+ *cmds: str,
327
+ pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
328
+ stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
329
+ stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
330
+ timeout: typing.Optional[int] = None,
331
+ workdir: typing.Optional[str] = None,
332
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
333
+ text: typing.Literal[False] = False,
334
+ bufsize: typing.Literal[-1, 1] = -1,
335
+ _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
336
+ ) -> modal.container_process.ContainerProcess[bytes]: ...
337
+ @typing.overload
338
+ async def aio(
339
+ self,
340
+ *cmds: str,
341
+ pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
342
+ stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
343
+ stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
344
+ timeout: typing.Optional[int] = None,
345
+ workdir: typing.Optional[str] = None,
346
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
347
+ text: typing.Literal[True] = True,
348
+ bufsize: typing.Literal[-1, 1] = -1,
349
+ _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
350
+ ) -> modal.container_process.ContainerProcess[str]: ...
351
+ @typing.overload
352
+ async def aio(
353
+ self,
354
+ *cmds: str,
355
+ pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
356
+ stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
357
+ stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
358
+ timeout: typing.Optional[int] = None,
359
+ workdir: typing.Optional[str] = None,
360
+ secrets: collections.abc.Sequence[modal.secret.Secret] = (),
361
+ text: typing.Literal[False] = False,
362
+ bufsize: typing.Literal[-1, 1] = -1,
363
+ _pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
364
+ ) -> modal.container_process.ContainerProcess[bytes]: ...
365
+
366
+ exec: __exec_spec
367
+
368
+ class __open_spec(typing_extensions.Protocol):
369
+ @typing.overload
370
+ def __call__(self, path: str, mode: _typeshed.OpenTextMode) -> modal.file_io.FileIO[str]: ...
371
+ @typing.overload
372
+ def __call__(self, path: str, mode: _typeshed.OpenBinaryMode) -> modal.file_io.FileIO[bytes]: ...
373
+ @typing.overload
374
+ async def aio(self, path: str, mode: _typeshed.OpenTextMode) -> modal.file_io.FileIO[str]: ...
375
+ @typing.overload
376
+ async def aio(self, path: str, mode: _typeshed.OpenBinaryMode) -> modal.file_io.FileIO[bytes]: ...
377
+
378
+ open: __open_spec
379
+
380
+ class __ls_spec(typing_extensions.Protocol):
381
+ def __call__(self, path: str) -> list[str]: ...
382
+ async def aio(self, path: str) -> list[str]: ...
383
+
384
+ ls: __ls_spec
385
+
386
+ class __mkdir_spec(typing_extensions.Protocol):
387
+ def __call__(self, path: str, parents: bool = False) -> None: ...
388
+ async def aio(self, path: str, parents: bool = False) -> None: ...
389
+
390
+ mkdir: __mkdir_spec
391
+
392
+ class __rm_spec(typing_extensions.Protocol):
393
+ def __call__(self, path: str, recursive: bool = False) -> None: ...
394
+ async def aio(self, path: str, recursive: bool = False) -> None: ...
395
+
396
+ rm: __rm_spec
397
+
398
+ class __watch_spec(typing_extensions.Protocol):
399
+ def __call__(
400
+ self,
401
+ path: str,
402
+ filter: typing.Optional[list[modal.file_io.FileWatchEventType]] = None,
403
+ recursive: typing.Optional[bool] = None,
404
+ timeout: typing.Optional[int] = None,
405
+ ) -> typing.Iterator[modal.file_io.FileWatchEvent]: ...
406
+ def aio(
407
+ self,
408
+ path: str,
409
+ filter: typing.Optional[list[modal.file_io.FileWatchEventType]] = None,
410
+ recursive: typing.Optional[bool] = None,
411
+ timeout: typing.Optional[int] = None,
412
+ ) -> typing.AsyncIterator[modal.file_io.FileWatchEvent]: ...
413
+
414
+ watch: __watch_spec
206
415
 
207
416
  @property
208
- def stderr(self) -> LogsReader:
209
- ...
210
-
417
+ def stdout(self) -> modal.io_streams.StreamReader[str]: ...
211
418
  @property
212
- def stdin(self) -> StreamWriter:
213
- ...
214
-
419
+ def stderr(self) -> modal.io_streams.StreamReader[str]: ...
420
+ @property
421
+ def stdin(self) -> modal.io_streams.StreamWriter: ...
215
422
  @property
216
- def returncode(self) -> typing.Union[int, None]:
217
- ...
423
+ def returncode(self) -> typing.Optional[int]: ...
424
+
425
+ class __list_spec(typing_extensions.Protocol):
426
+ def __call__(
427
+ self,
428
+ *,
429
+ app_id: typing.Optional[str] = None,
430
+ tags: typing.Optional[dict[str, str]] = None,
431
+ client: typing.Optional[modal.client.Client] = None,
432
+ ) -> typing.Generator[Sandbox, None, None]: ...
433
+ def aio(
434
+ self,
435
+ *,
436
+ app_id: typing.Optional[str] = None,
437
+ tags: typing.Optional[dict[str, str]] = None,
438
+ client: typing.Optional[modal.client.Client] = None,
439
+ ) -> collections.abc.AsyncGenerator[Sandbox, None]: ...
440
+
441
+ list: __list_spec
442
+
443
+ def __getattr__(name): ...
444
+
445
+ _default_image: modal.image._Image
modal/schedule.py CHANGED
@@ -19,10 +19,10 @@ class Cron(Schedule):
19
19
 
20
20
  ```python
21
21
  import modal
22
- stub = modal.Stub()
22
+ app = modal.App()
23
23
 
24
24
 
25
- @stub.function(schedule=modal.Cron("* * * * *"))
25
+ @app.function(schedule=modal.Cron("* * * * *"))
26
26
  def f():
27
27
  print("This function will run every minute")
28
28
  ```
@@ -49,9 +49,9 @@ class Period(Schedule):
49
49
 
50
50
  ```python
51
51
  import modal
52
- stub = modal.Stub()
52
+ app = modal.App()
53
53
 
54
- @stub.function(schedule=modal.Period(days=1))
54
+ @app.function(schedule=modal.Period(days=1))
55
55
  def f():
56
56
  print("This function will run every day")
57
57
 
@@ -1,5 +1,6 @@
1
1
  # Copyright Modal Labs 2024
2
- from typing import Optional
2
+ from collections.abc import Sequence
3
+ from typing import Optional, Union
3
4
 
4
5
  from modal_proto import api_pb2
5
6
 
@@ -11,17 +12,33 @@ class SchedulerPlacement:
11
12
 
12
13
  def __init__(
13
14
  self,
14
- region: Optional[str] = None,
15
+ region: Optional[Union[str, Sequence[str]]] = None,
15
16
  zone: Optional[str] = None,
16
17
  spot: Optional[bool] = None,
18
+ instance_type: Optional[Union[str, Sequence[str]]] = None,
17
19
  ):
18
20
  """mdmd:hidden"""
19
21
  _lifecycle: Optional[str] = None
20
22
  if spot is not None:
21
23
  _lifecycle = "spot" if spot else "on-demand"
22
24
 
25
+ regions = []
26
+ if region:
27
+ if isinstance(region, str):
28
+ regions = [region]
29
+ else:
30
+ regions = list(region)
31
+
32
+ instance_types = []
33
+ if instance_type:
34
+ if isinstance(instance_type, str):
35
+ instance_types = [instance_type]
36
+ else:
37
+ instance_types = list(instance_type)
38
+
23
39
  self.proto = api_pb2.SchedulerPlacement(
24
- _region=region,
40
+ regions=regions,
25
41
  _zone=zone,
26
42
  _lifecycle=_lifecycle,
43
+ _instance_types=instance_types,
27
44
  )