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
test/cpu_test.py DELETED
@@ -1,23 +0,0 @@
1
- # Copyright Modal Labs 2023
2
- import pytest
3
-
4
- from modal import Stub
5
- from modal.exception import InvalidError
6
-
7
-
8
- def dummy():
9
- pass
10
-
11
-
12
- def test_cpu_lower_bound(client, servicer):
13
- stub = Stub()
14
-
15
- stub.function(cpu=0.0)(dummy)
16
-
17
- with pytest.raises(InvalidError):
18
- with stub.run(client=client):
19
- pass
20
-
21
- stub.function(cpu=42)(dummy)
22
- with stub.run(client=client):
23
- pass
test/decorator_test.py DELETED
@@ -1,85 +0,0 @@
1
- # Copyright Modal Labs 2023
2
- import pytest
3
-
4
- from modal import Stub, asgi_app, method, web_endpoint, wsgi_app
5
- from modal.exception import InvalidError
6
-
7
-
8
- def test_local_entrypoint_forgot_parentheses():
9
- stub = Stub()
10
-
11
- with pytest.raises(InvalidError, match="local_entrypoint()"):
12
-
13
- @stub.local_entrypoint # type: ignore
14
- def f():
15
- pass
16
-
17
-
18
- def test_function_forgot_parentheses():
19
- stub = Stub()
20
-
21
- with pytest.raises(InvalidError, match="function()"):
22
-
23
- @stub.function # type: ignore
24
- def f():
25
- pass
26
-
27
-
28
- def test_cls_forgot_parentheses():
29
- stub = Stub()
30
-
31
- with pytest.raises(InvalidError, match="cls()"):
32
-
33
- @stub.cls # type: ignore
34
- class XYZ:
35
- pass
36
-
37
-
38
- def test_method_forgot_parentheses():
39
- stub = Stub()
40
-
41
- with pytest.raises(InvalidError, match="method()"):
42
-
43
- @stub.cls()
44
- class XYZ:
45
- @method # type: ignore
46
- def f(self):
47
- pass
48
-
49
-
50
- def test_invalid_web_decorator_usage():
51
- stub = Stub()
52
-
53
- with pytest.raises(InvalidError, match="web_endpoint()"):
54
-
55
- @stub.function() # type: ignore
56
- @web_endpoint # type: ignore
57
- def my_handle():
58
- pass
59
-
60
- with pytest.raises(InvalidError, match="asgi_app()"):
61
-
62
- @stub.function() # type: ignore
63
- @asgi_app # type: ignore
64
- def my_handle_asgi():
65
- pass
66
-
67
- with pytest.raises(InvalidError, match="wsgi_app()"):
68
-
69
- @stub.function() # type: ignore
70
- @wsgi_app # type: ignore
71
- def my_handle_wsgi():
72
- pass
73
-
74
-
75
- def test_web_endpoint_method():
76
- stub = Stub()
77
-
78
- with pytest.raises(InvalidError, match="remove the `@method`"):
79
-
80
- @stub.cls()
81
- class Container:
82
- @method() # type: ignore
83
- @web_endpoint()
84
- def generate(self):
85
- pass
test/deprecation_test.py DELETED
@@ -1,34 +0,0 @@
1
- # Copyright Modal Labs 2022
2
- import pytest
3
-
4
- from modal.exception import DeprecationError
5
-
6
- from .supports.functions import deprecated_function
7
-
8
- # Not a pytest unit test, but an extra assertion that we catch issues in global scope too
9
- # See #2228
10
- exc = None
11
- try:
12
- deprecated_function(42)
13
- except Exception as e:
14
- exc = e
15
- finally:
16
- assert isinstance(exc, DeprecationError) # If you see this, try running `pytest client/client_test`
17
-
18
-
19
- def test_deprecation():
20
- # See conftest.py in the root of the repo
21
- # All deprecation warnings in modal during tests will trigger exceptions
22
- with pytest.raises(DeprecationError):
23
- deprecated_function(42)
24
-
25
- # With this context manager, it doesn't raise an exception, but we record
26
- # the warning. This is the normal behavior outside of pytest.
27
- with pytest.warns(DeprecationError) as record:
28
- res = deprecated_function(42)
29
- assert res == 1764
30
-
31
- # Make sure it raises in the right file
32
- from .supports import functions
33
-
34
- assert record[0].filename == functions.__file__
test/dict_test.py DELETED
@@ -1,33 +0,0 @@
1
- # Copyright Modal Labs 2022
2
- import pytest
3
- import time
4
-
5
- from modal import Dict
6
-
7
-
8
- def test_dict_app(servicer, client):
9
- d = Dict.lookup("my-amazing-dict", {"xyz": 123}, create_if_missing=True, client=client)
10
- d["foo"] = 42
11
- d["foo"] += 5
12
- assert d["foo"] == 47
13
- assert d.len() == 2
14
-
15
- d.clear()
16
- assert d.len() == 0
17
- with pytest.raises(KeyError):
18
- _ = d["foo"]
19
-
20
- assert d.get("foo", default=True)
21
- d["foo"] = None
22
- assert d["foo"] is None
23
-
24
-
25
- def test_dict_ephemeral(servicer, client):
26
- assert servicer.n_dict_heartbeats == 0
27
- with Dict.ephemeral({"bar": 123}, client=client, _heartbeat_sleep=1) as d:
28
- d["foo"] = 42
29
- assert d.len() == 2
30
- assert d["foo"] == 42
31
- assert d["bar"] == 123
32
- time.sleep(1.5) # Make time for 2 heartbeats
33
- assert servicer.n_dict_heartbeats == 2
test/e2e_test.py DELETED
@@ -1,68 +0,0 @@
1
- # Copyright Modal Labs 2022
2
- import os
3
- import pathlib
4
- import subprocess
5
- import sys
6
- from typing import Tuple
7
-
8
-
9
- def _cli(args, server_url, extra_env={}, check=True) -> Tuple[int, str, str]:
10
- lib_dir = pathlib.Path(__file__).parent.parent
11
- args = [sys.executable] + args
12
- env = {
13
- "MODAL_SERVER_URL": server_url,
14
- **os.environ,
15
- "PYTHONUTF8": "1", # For windows
16
- **extra_env,
17
- }
18
- ret = subprocess.run(args, cwd=lib_dir, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
19
-
20
- stdout = ret.stdout.decode()
21
- stderr = ret.stderr.decode()
22
- if check and ret.returncode != 0:
23
- raise Exception(f"Failed with {ret.returncode} stdout: {stdout} stderr: {stderr}")
24
- return ret.returncode, stdout, stderr
25
-
26
-
27
- def test_run_e2e(servicer):
28
- _, _, err = _cli(["-m", "test.supports.script"], servicer.remote_addr)
29
- assert err == ""
30
-
31
-
32
- def test_run_progress_info(servicer):
33
- returncode, stdout, stderr = _cli(["-m", "test.supports.progress_info"], servicer.remote_addr)
34
- assert returncode == 0
35
- assert stderr == ""
36
- lines = stdout.splitlines()
37
- assert "Initialized. View run at https://modaltest.com/apps/ap-123" in lines[0]
38
- assert "App completed" in lines[-1]
39
-
40
-
41
- def test_run_profiler(servicer):
42
- _cli(["-m", "cProfile", "-m", "test.supports.script"], servicer.remote_addr)
43
-
44
-
45
- def test_run_unconsumed_map(servicer):
46
- _, _, err = _cli(["-m", "test.supports.unconsumed_map"], servicer.remote_addr)
47
- assert "map" in err
48
- assert "for-loop" in err
49
-
50
- _, _, err = _cli(["-m", "test.supports.consumed_map"], servicer.remote_addr)
51
- assert "map" not in err
52
- assert "for-loop" not in err
53
-
54
-
55
- def test_auth_failure_last_line(servicer):
56
- returncode, out, err = _cli(
57
- ["-m", "test.supports.script"],
58
- servicer.remote_addr,
59
- extra_env={"MODAL_TOKEN_ID": "bad", "MODAL_TOKEN_SECRET": "bad"},
60
- check=False,
61
- )
62
- try:
63
- assert returncode != 0
64
- assert "bad bad bad" in err.strip().split("\n")[-1] # err msg should be on the last line
65
- except Exception:
66
- print("out:", repr(out))
67
- print("err:", repr(err))
68
- raise
test/error_test.py DELETED
@@ -1,7 +0,0 @@
1
- # Copyright Modal Labs 2022
2
- from modal import Error
3
- from modal.exception import NotFoundError
4
-
5
-
6
- def test_modal_errors():
7
- assert issubclass(NotFoundError, Error)
@@ -1,32 +0,0 @@
1
- # Copyright Modal Labs 2023
2
- import pytest
3
-
4
- from modal import Stub
5
- from modal._serialization import deserialize
6
-
7
-
8
- @pytest.mark.asyncio
9
- async def test_serialize_deserialize_function(servicer, client):
10
- stub = Stub()
11
-
12
- @stub.function(serialized=True, name="foo")
13
- def foo():
14
- 2 * foo.remote()
15
-
16
- assert foo.object_id is None
17
-
18
- with stub.run(client=client):
19
- object_id = foo.object_id
20
-
21
- assert object_id is not None
22
- assert {object_id} == servicer.precreated_functions
23
-
24
- foo_def = servicer.app_functions[object_id]
25
-
26
- assert len(servicer.client_calls) == 0
27
-
28
- deserialized_function_body = deserialize(foo_def.function_serialized, client)
29
- deserialized_function_body() # call locally as if in container, this should trigger a "remote" foo() call
30
- assert len(servicer.client_calls) == 1
31
- function_call_id = list(servicer.client_calls.keys())[0]
32
- assert servicer.function_id_for_function_call[function_call_id] == object_id