modal 0.62.115__py3-none-any.whl → 0.72.13__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 +402 -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 +1025 -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 +196 -0
  84. modal/functions.py +846 -428
  85. modal/functions.pyi +446 -387
  86. modal/gpu.py +57 -44
  87. modal/image.py +943 -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.13.dist-info}/METADATA +5 -5
  128. modal-0.72.13.dist-info/RECORD +174 -0
  129. {modal-0.62.115.dist-info → modal-0.72.13.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.13.dist-info}/LICENSE +0 -0
  219. {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/WHEEL +0 -0
  220. {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/entry_points.txt +0 -0
test/watcher_test.py DELETED
@@ -1,58 +0,0 @@
1
- # Copyright Modal Labs 2023
2
- import pytest
3
- import random
4
- import string
5
- import sys
6
- from pathlib import Path
7
-
8
- from watchfiles import Change
9
-
10
- import modal
11
- from modal._watcher import _watch_args_from_mounts
12
- from modal.mount import Mount, _Mount
13
-
14
-
15
- @pytest.mark.asyncio
16
- async def test__watch_args_from_mounts(monkeypatch, test_dir):
17
- paths, watch_filter = _watch_args_from_mounts(
18
- mounts=[
19
- _Mount.from_local_file("/x/foo.py", remote_path="/foo.py"),
20
- _Mount.from_local_dir("/one/two/bucklemyshoe", remote_path="/"),
21
- _Mount.from_local_dir("/x/z", remote_path="/z"),
22
- ]
23
- )
24
-
25
- assert paths == {Path("/x").absolute(), Path("/one/two/bucklemyshoe").absolute(), Path("/x/z").absolute()}
26
- assert watch_filter(Change.modified, "/x/foo.py")
27
- assert not watch_filter(Change.modified, "/x/notwatched.py")
28
- assert not watch_filter(Change.modified, "/x/y/foo.py")
29
- assert watch_filter(Change.modified, "/x/z/bar.py")
30
- random_filename = "".join(random.choices(string.ascii_uppercase + string.digits, k=10))
31
- assert watch_filter(Change.modified, f"/one/two/bucklemyshoe/{random_filename}")
32
- assert not watch_filter(Change.modified, "/one/two/bucklemyshoe/.DS_Store")
33
-
34
-
35
- def dummy():
36
- pass
37
-
38
-
39
- @pytest.fixture()
40
- def clean_sys_modules(monkeypatch):
41
- # run test assuming no user-defined modules have been loaded
42
- module_names = set()
43
- for name, mod in sys.modules.items():
44
- if getattr(mod, "__file__", None) and not ("/lib/" in mod.__file__ or "/site-packages/" in mod.__file__):
45
- module_names.add(name)
46
-
47
- for m in module_names:
48
- monkeypatch.delitem(sys.modules, m)
49
-
50
-
51
- @pytest.mark.usefixtures("clean_sys_modules")
52
- @pytest.mark.skip("not working in ci for some reason. deactivating for now") # TODO(elias) fix
53
- def test_watch_mounts_ignore_local():
54
- app = modal.App()
55
- app.function(mounts=[Mount.from_name("some-published-mount")])(dummy)
56
-
57
- mounts = app._get_watch_mounts()
58
- assert len(mounts) == 0
test/webhook_test.py DELETED
@@ -1,145 +0,0 @@
1
- # Copyright Modal Labs 2022
2
- import pathlib
3
- import pytest
4
- import subprocess
5
- import sys
6
-
7
- from fastapi.testclient import TestClient
8
-
9
- from modal import App, asgi_app, web_endpoint, wsgi_app
10
- from modal._asgi import webhook_asgi_app
11
- from modal.exception import InvalidError
12
- from modal.functions import Function
13
- from modal.running_app import RunningApp
14
- from modal_proto import api_pb2
15
-
16
- app = App()
17
-
18
-
19
- @app.function(cpu=42)
20
- @web_endpoint(method="PATCH")
21
- async def f(x):
22
- return {"square": x**2}
23
-
24
-
25
- @pytest.mark.asyncio
26
- async def test_webhook(servicer, client, reset_container_app):
27
- async with app.run(client=client):
28
- assert f.web_url
29
-
30
- assert servicer.app_functions["fu-1"].webhook_config.type == api_pb2.WEBHOOK_TYPE_FUNCTION
31
- assert servicer.app_functions["fu-1"].webhook_config.method == "PATCH"
32
-
33
- # Make sure we can call the webhooks
34
- # TODO: reinstate `.remote` check when direct webhook fn invocation is fixed.
35
- # assert await f.remote(10)
36
- assert await f.local(100) == {"square": 10000}
37
-
38
- # Make sure the container gets the app id as well
39
- container_app = RunningApp(app_id=app.app_id)
40
- app._init_container(client, container_app)
41
- assert isinstance(f, Function)
42
- assert f.web_url
43
-
44
-
45
- def test_webhook_cors():
46
- def handler():
47
- return {"message": "Hello, World!"}
48
-
49
- app = webhook_asgi_app(handler, method="GET")
50
- client = TestClient(app)
51
- resp = client.options(
52
- "/",
53
- headers={
54
- "Origin": "http://example.com",
55
- "Access-Control-Request-Method": "POST",
56
- },
57
- )
58
- assert resp.headers["Access-Control-Allow-Origin"] == "http://example.com"
59
-
60
- assert client.get("/").json() == {"message": "Hello, World!"}
61
- assert client.post("/").status_code == 405 # Method Not Allowed
62
-
63
-
64
- @pytest.mark.asyncio
65
- async def test_webhook_no_docs():
66
- # FastAPI automatically sets docs URLs for apps, which we disable because it
67
- # can be unexpected for users who are unfamilar with FastAPI.
68
- #
69
- # https://fastapi.tiangolo.com/tutorial/metadata/#docs-urls
70
-
71
- def handler():
72
- return {"message": "Hello, World!"}
73
-
74
- app = webhook_asgi_app(handler, method="GET")
75
- client = TestClient(app)
76
- assert client.get("/docs").status_code == 404
77
- assert client.get("/redoc").status_code == 404
78
-
79
-
80
- def test_webhook_generator():
81
- app = App()
82
-
83
- with pytest.raises(InvalidError) as excinfo:
84
-
85
- @app.function(serialized=True)
86
- @web_endpoint()
87
- def web_gen():
88
- yield None
89
-
90
- assert "streaming" in str(excinfo.value).lower()
91
-
92
-
93
- @pytest.mark.asyncio
94
- async def test_webhook_forgot_function(servicer, client):
95
- lib_dir = pathlib.Path(__file__).parent.parent
96
- args = [sys.executable, "-m", "test.supports.webhook_forgot_function"]
97
- ret = subprocess.run(args, cwd=lib_dir, stderr=subprocess.PIPE)
98
- stderr = ret.stderr.decode()
99
- assert "absent_minded_function" in stderr
100
- assert "@app.function" in stderr
101
-
102
-
103
- @pytest.mark.asyncio
104
- async def test_webhook_decorator_in_wrong_order(servicer, client):
105
- app = App()
106
-
107
- with pytest.raises(InvalidError) as excinfo:
108
-
109
- @web_endpoint() # type: ignore
110
- @app.function(serialized=True)
111
- async def g(x):
112
- pass
113
-
114
- assert "wrong order" in str(excinfo.value).lower()
115
-
116
-
117
- @pytest.mark.asyncio
118
- async def test_asgi_wsgi(servicer, client):
119
- app = App()
120
-
121
- @app.function(serialized=True)
122
- @asgi_app()
123
- async def my_asgi(x):
124
- pass
125
-
126
- @app.function(serialized=True)
127
- @wsgi_app()
128
- async def my_wsgi(x):
129
- pass
130
-
131
- async with app.run(client=client):
132
- pass
133
-
134
- assert len(servicer.app_functions) == 2
135
- assert servicer.app_functions["fu-1"].webhook_config.type == api_pb2.WEBHOOK_TYPE_ASGI_APP
136
- assert servicer.app_functions["fu-2"].webhook_config.type == api_pb2.WEBHOOK_TYPE_WSGI_APP
137
-
138
-
139
- def test_positional_method(servicer, client):
140
- with pytest.raises(InvalidError, match="method="):
141
- web_endpoint("GET")
142
- with pytest.raises(InvalidError, match="label="):
143
- asgi_app("baz")
144
- with pytest.raises(InvalidError, match="label="):
145
- wsgi_app("baz")