modal 0.62.115__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 +13 -9
  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 +407 -398
  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 -60
  11. modal/_resources.py +26 -7
  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/{execution_context.py → _runtime/execution_context.py} +11 -2
  16. modal/_runtime/telemetry.py +169 -0
  17. modal/_runtime/user_code_imports.py +356 -0
  18. modal/_serialization.py +123 -6
  19. modal/_traceback.py +47 -187
  20. modal/_tunnel.py +50 -14
  21. modal/_tunnel.pyi +19 -36
  22. modal/_utils/app_utils.py +3 -17
  23. modal/_utils/async_utils.py +386 -104
  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 +299 -98
  29. modal/_utils/grpc_testing.py +47 -34
  30. modal/_utils/grpc_utils.py +54 -21
  31. modal/_utils/hash_utils.py +51 -10
  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 +3 -3
  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 +12 -10
  43. modal/app.py +561 -323
  44. modal/app.pyi +474 -262
  45. modal/call_graph.py +7 -6
  46. modal/cli/_download.py +22 -6
  47. modal/cli/_traceback.py +200 -0
  48. modal/cli/app.py +203 -42
  49. modal/cli/config.py +12 -5
  50. modal/cli/container.py +61 -13
  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 +21 -48
  55. modal/cli/launch.py +28 -14
  56. modal/cli/network_file_system.py +57 -21
  57. modal/cli/profile.py +1 -1
  58. modal/cli/programs/run_jupyter.py +34 -9
  59. modal/cli/programs/vscode.py +58 -8
  60. modal/cli/queues.py +131 -0
  61. modal/cli/run.py +199 -96
  62. modal/cli/secret.py +5 -4
  63. modal/cli/token.py +7 -2
  64. modal/cli/utils.py +74 -8
  65. modal/cli/volume.py +97 -56
  66. modal/client.py +248 -144
  67. modal/client.pyi +156 -124
  68. modal/cloud_bucket_mount.py +43 -30
  69. modal/cloud_bucket_mount.pyi +32 -25
  70. modal/cls.py +528 -141
  71. modal/cls.pyi +189 -145
  72. modal/config.py +32 -15
  73. modal/container_process.py +177 -0
  74. modal/container_process.pyi +82 -0
  75. modal/dict.py +50 -54
  76. modal/dict.pyi +120 -164
  77. modal/environments.py +106 -5
  78. modal/environments.pyi +77 -25
  79. modal/exception.py +30 -43
  80. modal/experimental.py +62 -2
  81. modal/file_io.py +537 -0
  82. modal/file_io.pyi +235 -0
  83. modal/file_pattern_matcher.py +197 -0
  84. modal/functions.py +846 -428
  85. modal/functions.pyi +446 -387
  86. modal/gpu.py +57 -44
  87. modal/image.py +946 -417
  88. modal/image.pyi +584 -245
  89. modal/io_streams.py +434 -0
  90. modal/io_streams.pyi +122 -0
  91. modal/mount.py +223 -90
  92. modal/mount.pyi +241 -243
  93. modal/network_file_system.py +85 -86
  94. modal/network_file_system.pyi +151 -110
  95. modal/object.py +66 -36
  96. modal/object.pyi +166 -143
  97. modal/output.py +63 -0
  98. modal/parallel_map.py +73 -47
  99. modal/parallel_map.pyi +51 -63
  100. modal/partial_function.py +272 -107
  101. modal/partial_function.pyi +219 -120
  102. modal/proxy.py +15 -12
  103. modal/proxy.pyi +3 -8
  104. modal/queue.py +96 -72
  105. modal/queue.pyi +210 -135
  106. modal/requirements/2024.04.txt +2 -1
  107. modal/requirements/2024.10.txt +16 -0
  108. modal/requirements/README.md +21 -0
  109. modal/requirements/base-images.json +22 -0
  110. modal/retries.py +45 -4
  111. modal/runner.py +325 -203
  112. modal/runner.pyi +124 -110
  113. modal/running_app.py +27 -4
  114. modal/sandbox.py +509 -231
  115. modal/sandbox.pyi +396 -169
  116. modal/schedule.py +2 -2
  117. modal/scheduler_placement.py +20 -3
  118. modal/secret.py +41 -25
  119. modal/secret.pyi +62 -42
  120. modal/serving.py +39 -49
  121. modal/serving.pyi +37 -43
  122. modal/stream_type.py +15 -0
  123. modal/token_flow.py +5 -3
  124. modal/token_flow.pyi +37 -32
  125. modal/volume.py +123 -137
  126. modal/volume.pyi +228 -221
  127. {modal-0.62.115.dist-info → modal-0.72.11.dist-info}/METADATA +5 -5
  128. modal-0.72.11.dist-info/RECORD +174 -0
  129. {modal-0.62.115.dist-info → modal-0.72.11.dist-info}/top_level.txt +0 -1
  130. modal_docs/gen_reference_docs.py +3 -1
  131. modal_docs/mdmd/mdmd.py +0 -1
  132. modal_docs/mdmd/signatures.py +1 -2
  133. modal_global_objects/images/base_images.py +28 -0
  134. modal_global_objects/mounts/python_standalone.py +2 -2
  135. modal_proto/__init__.py +1 -1
  136. modal_proto/api.proto +1231 -531
  137. modal_proto/api_grpc.py +750 -430
  138. modal_proto/api_pb2.py +2102 -1176
  139. modal_proto/api_pb2.pyi +8859 -0
  140. modal_proto/api_pb2_grpc.py +1329 -675
  141. modal_proto/api_pb2_grpc.pyi +1416 -0
  142. modal_proto/modal_api_grpc.py +149 -0
  143. modal_proto/modal_options_grpc.py +3 -0
  144. modal_proto/options_pb2.pyi +20 -0
  145. modal_proto/options_pb2_grpc.pyi +7 -0
  146. modal_proto/py.typed +0 -0
  147. modal_version/__init__.py +1 -1
  148. modal_version/_version_generated.py +2 -2
  149. modal/_asgi.py +0 -370
  150. modal/_container_exec.py +0 -128
  151. modal/_container_io_manager.py +0 -646
  152. modal/_container_io_manager.pyi +0 -412
  153. modal/_sandbox_shell.py +0 -49
  154. modal/app_utils.py +0 -20
  155. modal/app_utils.pyi +0 -17
  156. modal/execution_context.pyi +0 -37
  157. modal/shared_volume.py +0 -23
  158. modal/shared_volume.pyi +0 -24
  159. modal-0.62.115.dist-info/RECORD +0 -207
  160. modal_global_objects/images/conda.py +0 -15
  161. modal_global_objects/images/debian_slim.py +0 -15
  162. modal_global_objects/images/micromamba.py +0 -15
  163. test/__init__.py +0 -1
  164. test/aio_test.py +0 -12
  165. test/async_utils_test.py +0 -279
  166. test/blob_test.py +0 -67
  167. test/cli_imports_test.py +0 -149
  168. test/cli_test.py +0 -674
  169. test/client_test.py +0 -203
  170. test/cloud_bucket_mount_test.py +0 -22
  171. test/cls_test.py +0 -636
  172. test/config_test.py +0 -149
  173. test/conftest.py +0 -1485
  174. test/container_app_test.py +0 -50
  175. test/container_test.py +0 -1405
  176. test/cpu_test.py +0 -23
  177. test/decorator_test.py +0 -85
  178. test/deprecation_test.py +0 -34
  179. test/dict_test.py +0 -51
  180. test/e2e_test.py +0 -68
  181. test/error_test.py +0 -7
  182. test/function_serialization_test.py +0 -32
  183. test/function_test.py +0 -791
  184. test/function_utils_test.py +0 -101
  185. test/gpu_test.py +0 -159
  186. test/grpc_utils_test.py +0 -82
  187. test/helpers.py +0 -47
  188. test/image_test.py +0 -814
  189. test/live_reload_test.py +0 -80
  190. test/lookup_test.py +0 -70
  191. test/mdmd_test.py +0 -329
  192. test/mount_test.py +0 -162
  193. test/mounted_files_test.py +0 -327
  194. test/network_file_system_test.py +0 -188
  195. test/notebook_test.py +0 -66
  196. test/object_test.py +0 -41
  197. test/package_utils_test.py +0 -25
  198. test/queue_test.py +0 -115
  199. test/resolver_test.py +0 -59
  200. test/retries_test.py +0 -67
  201. test/runner_test.py +0 -85
  202. test/sandbox_test.py +0 -191
  203. test/schedule_test.py +0 -15
  204. test/scheduler_placement_test.py +0 -57
  205. test/secret_test.py +0 -89
  206. test/serialization_test.py +0 -50
  207. test/stub_composition_test.py +0 -10
  208. test/stub_test.py +0 -361
  209. test/test_asgi_wrapper.py +0 -234
  210. test/token_flow_test.py +0 -18
  211. test/traceback_test.py +0 -135
  212. test/tunnel_test.py +0 -29
  213. test/utils_test.py +0 -88
  214. test/version_test.py +0 -14
  215. test/volume_test.py +0 -397
  216. test/watcher_test.py +0 -58
  217. test/webhook_test.py +0 -145
  218. {modal-0.62.115.dist-info → modal-0.72.11.dist-info}/LICENSE +0 -0
  219. {modal-0.62.115.dist-info → modal-0.72.11.dist-info}/WHEEL +0 -0
  220. {modal-0.62.115.dist-info → modal-0.72.11.dist-info}/entry_points.txt +0 -0
modal/sandbox.pyi CHANGED
@@ -1,218 +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
9
17
  import modal.scheduler_placement
10
18
  import modal.secret
19
+ import modal.stream_type
11
20
  import modal.volume
12
21
  import modal_proto.api_pb2
13
22
  import os
14
23
  import typing
15
24
  import typing_extensions
16
25
 
17
- class _LogsReader:
18
- def __init__(self, file_descriptor: int, sandbox_id: str, client: modal.client._Client) -> None:
19
- ...
20
-
21
- async def read(self) -> str:
22
- ...
23
-
24
- def _get_logs(self) -> typing.AsyncIterator[typing.Union[modal_proto.api_pb2.TaskLogs, None]]:
25
- ...
26
-
27
- def __aiter__(self):
28
- ...
29
-
30
- async def __anext__(self):
31
- ...
32
-
33
-
34
- class _StreamWriter:
35
- def __init__(self, sandbox_id: str, client: modal.client._Client):
36
- ...
37
-
38
- def get_next_index(self):
39
- ...
40
-
41
- def write(self, data: typing.Union[bytes, bytearray, memoryview]):
42
- ...
43
-
44
- def write_eof(self):
45
- ...
46
-
47
- async def drain(self):
48
- ...
49
-
50
-
51
- class LogsReader:
52
- def __init__(self, file_descriptor: int, sandbox_id: str, client: modal.client.Client) -> None:
53
- ...
54
-
55
- class __read_spec(typing_extensions.Protocol):
56
- def __call__(self) -> str:
57
- ...
58
-
59
- async def aio(self, *args, **kwargs) -> str:
60
- ...
61
-
62
- read: __read_spec
63
-
64
- class ___get_logs_spec(typing_extensions.Protocol):
65
- def __call__(self) -> typing.Iterator[typing.Union[modal_proto.api_pb2.TaskLogs, None]]:
66
- ...
67
-
68
- def aio(self) -> typing.AsyncIterator[typing.Union[modal_proto.api_pb2.TaskLogs, None]]:
69
- ...
70
-
71
- _get_logs: ___get_logs_spec
72
-
73
- def __iter__(self):
74
- ...
75
-
76
- def __aiter__(self):
77
- ...
78
-
79
- def __next__(self):
80
- ...
81
-
82
- async def __anext__(self, *args, **kwargs):
83
- ...
84
-
85
-
86
- class StreamWriter:
87
- def __init__(self, sandbox_id: str, client: modal.client.Client):
88
- ...
89
-
90
- def get_next_index(self):
91
- ...
92
-
93
- def write(self, data: typing.Union[bytes, bytearray, memoryview]):
94
- ...
95
-
96
- def write_eof(self):
97
- ...
98
-
99
- class __drain_spec(typing_extensions.Protocol):
100
- def __call__(self):
101
- ...
102
-
103
- async def aio(self, *args, **kwargs):
104
- ...
105
-
106
- drain: __drain_spec
107
-
108
-
109
26
  class _Sandbox(modal.object._Object):
110
- _result: typing.Union[modal_proto.api_pb2.GenericResult, None]
111
- _stdout: _LogsReader
112
- _stderr: _LogsReader
113
- _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]]
114
33
 
115
34
  @staticmethod
116
- 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, typing.Tuple[int, 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, _experimental_scheduler: bool = False, _experimental_scheduler_placement: typing.Union[modal.scheduler_placement.SchedulerPlacement, None] = None) -> _Sandbox:
117
- ...
118
-
119
- def _hydrate_metadata(self, handle_metadata: typing.Union[google.protobuf.message.Message, None]):
120
- ...
121
-
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: ...
122
60
  @staticmethod
123
- async def from_id(sandbox_id: str, client: typing.Union[modal.client._Client, None] = None) -> _Sandbox:
124
- ...
125
-
126
- async def wait(self, raise_on_termination: bool = True):
127
- ...
128
-
129
- async def terminate(self):
130
- ...
131
-
132
- async def poll(self) -> typing.Union[int, None]:
133
- ...
134
-
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]: ...
135
141
  @property
136
- def stdout(self) -> _LogsReader:
137
- ...
138
-
142
+ def stdout(self) -> modal.io_streams._StreamReader[str]: ...
139
143
  @property
140
- def stderr(self) -> _LogsReader:
141
- ...
142
-
144
+ def stderr(self) -> modal.io_streams._StreamReader[str]: ...
143
145
  @property
144
- def stdin(self) -> _StreamWriter:
145
- ...
146
-
146
+ def stdin(self) -> modal.io_streams._StreamWriter: ...
147
147
  @property
148
- def returncode(self) -> typing.Union[int, None]:
149
- ...
150
-
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]: ...
151
156
 
152
157
  class Sandbox(modal.object.Object):
153
- _result: typing.Union[modal_proto.api_pb2.GenericResult, None]
154
- _stdout: LogsReader
155
- _stderr: LogsReader
156
- _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]): ...
157
259
 
158
- def __init__(self, *args, **kwargs):
159
- ...
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: ...
160
263
 
161
- @staticmethod
162
- 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, typing.Tuple[int, 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, _experimental_scheduler: bool = False, _experimental_scheduler_placement: typing.Union[modal.scheduler_placement.SchedulerPlacement, None] = None) -> Sandbox:
163
- ...
264
+ from_id: __from_id_spec
164
265
 
165
- def _hydrate_metadata(self, handle_metadata: typing.Union[google.protobuf.message.Message, None]):
166
- ...
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): ...
167
269
 
168
- class __from_id_spec(typing_extensions.Protocol):
169
- def __call__(self, sandbox_id: str, client: typing.Union[modal.client.Client, None] = None) -> Sandbox:
170
- ...
270
+ set_tags: __set_tags_spec
171
271
 
172
- async def aio(self, *args, **kwargs) -> Sandbox:
173
- ...
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: ...
174
275
 
175
- from_id: __from_id_spec
276
+ snapshot_filesystem: __snapshot_filesystem_spec
176
277
 
177
278
  class __wait_spec(typing_extensions.Protocol):
178
- def __call__(self, raise_on_termination: bool = True):
179
- ...
180
-
181
- async def aio(self, *args, **kwargs):
182
- ...
279
+ def __call__(self, raise_on_termination: bool = True): ...
280
+ async def aio(self, raise_on_termination: bool = True): ...
183
281
 
184
282
  wait: __wait_spec
185
283
 
186
- class __terminate_spec(typing_extensions.Protocol):
187
- def __call__(self):
188
- ...
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]: ...
189
287
 
190
- async def aio(self, *args, **kwargs):
191
- ...
288
+ tunnels: __tunnels_spec
289
+
290
+ class __terminate_spec(typing_extensions.Protocol):
291
+ def __call__(self): ...
292
+ async def aio(self): ...
192
293
 
193
294
  terminate: __terminate_spec
194
295
 
195
296
  class __poll_spec(typing_extensions.Protocol):
196
- def __call__(self) -> typing.Union[int, None]:
197
- ...
198
-
199
- async def aio(self, *args, **kwargs) -> typing.Union[int, None]:
200
- ...
297
+ def __call__(self) -> typing.Optional[int]: ...
298
+ async def aio(self) -> typing.Optional[int]: ...
201
299
 
202
300
  poll: __poll_spec
203
301
 
204
- @property
205
- def stdout(self) -> LogsReader:
206
- ...
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
207
415
 
208
416
  @property
209
- def stderr(self) -> LogsReader:
210
- ...
211
-
417
+ def stdout(self) -> modal.io_streams.StreamReader[str]: ...
212
418
  @property
213
- def stdin(self) -> StreamWriter:
214
- ...
215
-
419
+ def stderr(self) -> modal.io_streams.StreamReader[str]: ...
420
+ @property
421
+ def stdin(self) -> modal.io_streams.StreamWriter: ...
216
422
  @property
217
- def returncode(self) -> typing.Union[int, None]:
218
- ...
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,7 +19,7 @@ class Cron(Schedule):
19
19
 
20
20
  ```python
21
21
  import modal
22
- app = modal.App() # Note: "app" was called "stub" up until April 2024
22
+ app = modal.App()
23
23
 
24
24
 
25
25
  @app.function(schedule=modal.Cron("* * * * *"))
@@ -49,7 +49,7 @@ class Period(Schedule):
49
49
 
50
50
  ```python
51
51
  import modal
52
- app = modal.App() # Note: "app" was called "stub" up until April 2024
52
+ app = modal.App()
53
53
 
54
54
  @app.function(schedule=modal.Period(days=1))
55
55
  def f():
@@ -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
  )