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/exception.py CHANGED
@@ -1,10 +1,6 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import random
3
3
  import signal
4
- import sys
5
- import warnings
6
- from datetime import date
7
- from typing import Tuple
8
4
 
9
5
 
10
6
  class Error(Exception):
@@ -58,6 +54,10 @@ class InteractiveTimeoutError(TimeoutError):
58
54
  """Raised when interactive frontends time out while trying to connect to a container."""
59
55
 
60
56
 
57
+ class OutputExpiredError(TimeoutError):
58
+ """Raised when the Output exceeds expiration and times out."""
59
+
60
+
61
61
  class AuthError(Error):
62
62
  """Raised when a client has missing or invalid authentication."""
63
63
 
@@ -82,6 +82,18 @@ class ExecutionError(Error):
82
82
  """Raised when something unexpected happened during runtime."""
83
83
 
84
84
 
85
+ class DeserializationError(Error):
86
+ """Raised to provide more context when an error is encountered during deserialization."""
87
+
88
+
89
+ class SerializationError(Error):
90
+ """Raised to provide more context when an error is encountered during serialization."""
91
+
92
+
93
+ class RequestSizeError(Error):
94
+ """Raised when an operation produces a gRPC request that is rejected by the server for being too large."""
95
+
96
+
85
97
  class DeprecationError(UserWarning):
86
98
  """UserWarning category emitted when a deprecated Modal feature or API is used."""
87
99
 
@@ -92,6 +104,16 @@ class PendingDeprecationError(UserWarning):
92
104
  """Soon to be deprecated feature. Only used intermittently because of multi-repo concerns."""
93
105
 
94
106
 
107
+ class ServerWarning(UserWarning):
108
+ """Warning originating from the Modal server and re-issued in client code."""
109
+
110
+
111
+ class InternalFailure(Error):
112
+ """
113
+ Retriable internal error.
114
+ """
115
+
116
+
95
117
  class _CliUserExecutionError(Exception):
96
118
  """mdmd:hidden
97
119
  Private wrapper for exceptions during when importing or running stubs from the CLI.
@@ -107,45 +129,6 @@ class _CliUserExecutionError(Exception):
107
129
  self.user_source = user_source
108
130
 
109
131
 
110
- # TODO(erikbern): we have something similready in function_utils.py
111
- _INTERNAL_MODULES = ["modal", "synchronicity"]
112
-
113
-
114
- def _is_internal_frame(frame):
115
- module = frame.f_globals["__name__"].split(".")[0]
116
- return module in _INTERNAL_MODULES
117
-
118
-
119
- def deprecation_error(deprecated_on: Tuple[int, int, int], msg: str):
120
- raise DeprecationError(f"Deprecated on {date(*deprecated_on)}: {msg}")
121
-
122
-
123
- def deprecation_warning(
124
- deprecated_on: Tuple[int, int, int], msg: str, pending: bool = False, show_source: bool = True
125
- ) -> None:
126
- """Utility for getting the proper stack entry.
127
-
128
- See the implementation of the built-in [warnings.warn](https://docs.python.org/3/library/warnings.html#available-functions).
129
- """
130
- filename, lineno = "<unknown>", 0
131
- if show_source:
132
- # Find the last non-Modal line that triggered the warning
133
- try:
134
- frame = sys._getframe()
135
- while frame is not None and _is_internal_frame(frame):
136
- frame = frame.f_back
137
- filename = frame.f_code.co_filename
138
- lineno = frame.f_lineno
139
- except ValueError:
140
- # Use the defaults from above
141
- pass
142
-
143
- warning_cls: type = PendingDeprecationError if pending else DeprecationError
144
-
145
- # This is a lower-level function that warnings.warn uses
146
- warnings.warn_explicit(f"{date(*deprecated_on)}: {msg}", warning_cls, filename, lineno)
147
-
148
-
149
132
  def _simulate_preemption_interrupt(signum, frame):
150
133
  signal.alarm(30) # simulate a SIGKILL after 30s
151
134
  raise KeyboardInterrupt("Simulated preemption interrupt from modal-client!")
@@ -194,3 +177,11 @@ class InputCancellation(BaseException):
194
177
 
195
178
  class ModuleNotMountable(Exception):
196
179
  pass
180
+
181
+
182
+ class ClientClosed(Error):
183
+ pass
184
+
185
+
186
+ class FilesystemExecutionError(Error):
187
+ """Raised when an unknown error is thrown during a container filesystem operation."""
modal/experimental.py CHANGED
@@ -1,10 +1,69 @@
1
1
  # Copyright Modal Labs 2022
2
+ from typing import (
3
+ Any,
4
+ Callable,
5
+ )
6
+
7
+ import modal._clustered_functions
8
+ from modal.functions import _Function
9
+
10
+ from ._runtime.container_io_manager import _ContainerIOManager
11
+ from .exception import (
12
+ InvalidError,
13
+ )
14
+ from .partial_function import _PartialFunction, _PartialFunctionFlags
2
15
 
3
16
 
4
17
  def stop_fetching_inputs():
5
18
  """Don't fetch any more inputs from the server, after the current one.
6
19
  The container will exit gracefully after the current input is processed."""
20
+ _ContainerIOManager.stop_fetching_inputs()
21
+
22
+
23
+ def get_local_input_concurrency():
24
+ """Get the container's local input concurrency.
25
+ If recently reduced to particular value, it can return a larger number than
26
+ set due to in-progress inputs."""
27
+ return _ContainerIOManager.get_input_concurrency()
28
+
29
+
30
+ def set_local_input_concurrency(concurrency: int):
31
+ """Set the container's local input concurrency. Dynamic concurrency will be disabled.
32
+ When setting to a smaller value, this method will not interrupt in-progress inputs.
33
+ """
34
+ _ContainerIOManager.set_input_concurrency(concurrency)
35
+
36
+
37
+ def clustered(size: int, broadcast: bool = True):
38
+ """Provision clusters of colocated and networked containers for the Function.
39
+
40
+ Parameters:
41
+ size: int
42
+ Number of containers spun up to handle each input.
43
+ broadcast: bool = True
44
+ If True, inputs will be sent simultaneously to each container. Otherwise,
45
+ inputs will be sent only to the rank-0 container, which is responsible for
46
+ delegating to the workers.
47
+ """
48
+
49
+ assert broadcast, "broadcast=False has not been implemented yet!"
50
+
51
+ if size <= 0:
52
+ raise ValueError("cluster size must be greater than 0")
53
+
54
+ def wrapper(raw_f: Callable[..., Any]) -> _PartialFunction:
55
+ if isinstance(raw_f, _Function):
56
+ raw_f = raw_f.get_raw_f()
57
+ raise InvalidError(
58
+ f"Applying decorators for {raw_f} in the wrong order!\nUsage:\n\n"
59
+ "@app.function()\n@modal.clustered()\ndef clustered_function():\n ..."
60
+ )
61
+ return _PartialFunction(
62
+ raw_f, _PartialFunctionFlags.FUNCTION | _PartialFunctionFlags.CLUSTERED, cluster_size=size
63
+ )
64
+
65
+ return wrapper
7
66
 
8
- from .app import _container_app
9
67
 
10
- _container_app.stop_fetching_inputs()
68
+ def get_cluster_info() -> modal._clustered_functions.ClusterInfo:
69
+ return modal._clustered_functions.get_cluster_info()
@@ -4,7 +4,7 @@ import logging
4
4
  import sys
5
5
  from typing import Any
6
6
 
7
- from modal import Stub
7
+ from modal import App
8
8
  from modal._utils.async_utils import run_coro_blocking
9
9
  from modal.config import config, logger
10
10
 
@@ -18,11 +18,11 @@ def load_ipython_extension(ipython):
18
18
  logger.addHandler(logging.StreamHandler(stream=sys.stdout))
19
19
  logger.setLevel(config["loglevel"])
20
20
 
21
- # Create a app and provide it in the IPython app
22
- stub = Stub()
23
- ipython.push({"stub": stub})
21
+ # Create an app and provide it in the IPython app
22
+ app = App()
23
+ ipython.push({"app": app})
24
24
 
25
- app_ctx = stub.run()
25
+ app_ctx = app.run()
26
26
 
27
27
  # Notebooks have an event loop present, but we want this function
28
28
  # to be blocking. This is fairly hacky.