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.
- modal/__init__.py +13 -9
- modal/__main__.py +41 -3
- modal/_clustered_functions.py +80 -0
- modal/_clustered_functions.pyi +22 -0
- modal/_container_entrypoint.py +402 -398
- modal/_ipython.py +3 -13
- modal/_location.py +17 -10
- modal/_output.py +243 -99
- modal/_pty.py +2 -2
- modal/_resolver.py +55 -60
- modal/_resources.py +26 -7
- modal/_runtime/__init__.py +1 -0
- modal/_runtime/asgi.py +519 -0
- modal/_runtime/container_io_manager.py +1025 -0
- modal/{execution_context.py → _runtime/execution_context.py} +11 -2
- modal/_runtime/telemetry.py +169 -0
- modal/_runtime/user_code_imports.py +356 -0
- modal/_serialization.py +123 -6
- modal/_traceback.py +47 -187
- modal/_tunnel.py +50 -14
- modal/_tunnel.pyi +19 -36
- modal/_utils/app_utils.py +3 -17
- modal/_utils/async_utils.py +386 -104
- modal/_utils/blob_utils.py +157 -186
- modal/_utils/bytes_io_segment_payload.py +97 -0
- modal/_utils/deprecation.py +89 -0
- modal/_utils/docker_utils.py +98 -0
- modal/_utils/function_utils.py +299 -98
- modal/_utils/grpc_testing.py +47 -34
- modal/_utils/grpc_utils.py +54 -21
- modal/_utils/hash_utils.py +51 -10
- modal/_utils/http_utils.py +39 -9
- modal/_utils/logger.py +2 -1
- modal/_utils/mount_utils.py +34 -16
- modal/_utils/name_utils.py +58 -0
- modal/_utils/package_utils.py +14 -1
- modal/_utils/pattern_utils.py +205 -0
- modal/_utils/rand_pb_testing.py +3 -3
- modal/_utils/shell_utils.py +15 -49
- modal/_vendor/a2wsgi_wsgi.py +62 -72
- modal/_vendor/cloudpickle.py +1 -1
- modal/_watcher.py +12 -10
- modal/app.py +561 -323
- modal/app.pyi +474 -262
- modal/call_graph.py +7 -6
- modal/cli/_download.py +22 -6
- modal/cli/_traceback.py +200 -0
- modal/cli/app.py +203 -42
- modal/cli/config.py +12 -5
- modal/cli/container.py +61 -13
- modal/cli/dict.py +128 -0
- modal/cli/entry_point.py +26 -13
- modal/cli/environment.py +40 -9
- modal/cli/import_refs.py +21 -48
- modal/cli/launch.py +28 -14
- modal/cli/network_file_system.py +57 -21
- modal/cli/profile.py +1 -1
- modal/cli/programs/run_jupyter.py +34 -9
- modal/cli/programs/vscode.py +58 -8
- modal/cli/queues.py +131 -0
- modal/cli/run.py +199 -96
- modal/cli/secret.py +5 -4
- modal/cli/token.py +7 -2
- modal/cli/utils.py +74 -8
- modal/cli/volume.py +97 -56
- modal/client.py +248 -144
- modal/client.pyi +156 -124
- modal/cloud_bucket_mount.py +43 -30
- modal/cloud_bucket_mount.pyi +32 -25
- modal/cls.py +528 -141
- modal/cls.pyi +189 -145
- modal/config.py +32 -15
- modal/container_process.py +177 -0
- modal/container_process.pyi +82 -0
- modal/dict.py +50 -54
- modal/dict.pyi +120 -164
- modal/environments.py +106 -5
- modal/environments.pyi +77 -25
- modal/exception.py +30 -43
- modal/experimental.py +62 -2
- modal/file_io.py +537 -0
- modal/file_io.pyi +235 -0
- modal/file_pattern_matcher.py +196 -0
- modal/functions.py +846 -428
- modal/functions.pyi +446 -387
- modal/gpu.py +57 -44
- modal/image.py +943 -417
- modal/image.pyi +584 -245
- modal/io_streams.py +434 -0
- modal/io_streams.pyi +122 -0
- modal/mount.py +223 -90
- modal/mount.pyi +241 -243
- modal/network_file_system.py +85 -86
- modal/network_file_system.pyi +151 -110
- modal/object.py +66 -36
- modal/object.pyi +166 -143
- modal/output.py +63 -0
- modal/parallel_map.py +73 -47
- modal/parallel_map.pyi +51 -63
- modal/partial_function.py +272 -107
- modal/partial_function.pyi +219 -120
- modal/proxy.py +15 -12
- modal/proxy.pyi +3 -8
- modal/queue.py +96 -72
- modal/queue.pyi +210 -135
- modal/requirements/2024.04.txt +2 -1
- modal/requirements/2024.10.txt +16 -0
- modal/requirements/README.md +21 -0
- modal/requirements/base-images.json +22 -0
- modal/retries.py +45 -4
- modal/runner.py +325 -203
- modal/runner.pyi +124 -110
- modal/running_app.py +27 -4
- modal/sandbox.py +509 -231
- modal/sandbox.pyi +396 -169
- modal/schedule.py +2 -2
- modal/scheduler_placement.py +20 -3
- modal/secret.py +41 -25
- modal/secret.pyi +62 -42
- modal/serving.py +39 -49
- modal/serving.pyi +37 -43
- modal/stream_type.py +15 -0
- modal/token_flow.py +5 -3
- modal/token_flow.pyi +37 -32
- modal/volume.py +123 -137
- modal/volume.pyi +228 -221
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/METADATA +5 -5
- modal-0.72.13.dist-info/RECORD +174 -0
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/top_level.txt +0 -1
- modal_docs/gen_reference_docs.py +3 -1
- modal_docs/mdmd/mdmd.py +0 -1
- modal_docs/mdmd/signatures.py +1 -2
- modal_global_objects/images/base_images.py +28 -0
- modal_global_objects/mounts/python_standalone.py +2 -2
- modal_proto/__init__.py +1 -1
- modal_proto/api.proto +1231 -531
- modal_proto/api_grpc.py +750 -430
- modal_proto/api_pb2.py +2102 -1176
- modal_proto/api_pb2.pyi +8859 -0
- modal_proto/api_pb2_grpc.py +1329 -675
- modal_proto/api_pb2_grpc.pyi +1416 -0
- modal_proto/modal_api_grpc.py +149 -0
- modal_proto/modal_options_grpc.py +3 -0
- modal_proto/options_pb2.pyi +20 -0
- modal_proto/options_pb2_grpc.pyi +7 -0
- modal_proto/py.typed +0 -0
- modal_version/__init__.py +1 -1
- modal_version/_version_generated.py +2 -2
- modal/_asgi.py +0 -370
- modal/_container_exec.py +0 -128
- modal/_container_io_manager.py +0 -646
- modal/_container_io_manager.pyi +0 -412
- modal/_sandbox_shell.py +0 -49
- modal/app_utils.py +0 -20
- modal/app_utils.pyi +0 -17
- modal/execution_context.pyi +0 -37
- modal/shared_volume.py +0 -23
- modal/shared_volume.pyi +0 -24
- modal-0.62.115.dist-info/RECORD +0 -207
- modal_global_objects/images/conda.py +0 -15
- modal_global_objects/images/debian_slim.py +0 -15
- modal_global_objects/images/micromamba.py +0 -15
- test/__init__.py +0 -1
- test/aio_test.py +0 -12
- test/async_utils_test.py +0 -279
- test/blob_test.py +0 -67
- test/cli_imports_test.py +0 -149
- test/cli_test.py +0 -674
- test/client_test.py +0 -203
- test/cloud_bucket_mount_test.py +0 -22
- test/cls_test.py +0 -636
- test/config_test.py +0 -149
- test/conftest.py +0 -1485
- test/container_app_test.py +0 -50
- test/container_test.py +0 -1405
- test/cpu_test.py +0 -23
- test/decorator_test.py +0 -85
- test/deprecation_test.py +0 -34
- test/dict_test.py +0 -51
- test/e2e_test.py +0 -68
- test/error_test.py +0 -7
- test/function_serialization_test.py +0 -32
- test/function_test.py +0 -791
- test/function_utils_test.py +0 -101
- test/gpu_test.py +0 -159
- test/grpc_utils_test.py +0 -82
- test/helpers.py +0 -47
- test/image_test.py +0 -814
- test/live_reload_test.py +0 -80
- test/lookup_test.py +0 -70
- test/mdmd_test.py +0 -329
- test/mount_test.py +0 -162
- test/mounted_files_test.py +0 -327
- test/network_file_system_test.py +0 -188
- test/notebook_test.py +0 -66
- test/object_test.py +0 -41
- test/package_utils_test.py +0 -25
- test/queue_test.py +0 -115
- test/resolver_test.py +0 -59
- test/retries_test.py +0 -67
- test/runner_test.py +0 -85
- test/sandbox_test.py +0 -191
- test/schedule_test.py +0 -15
- test/scheduler_placement_test.py +0 -57
- test/secret_test.py +0 -89
- test/serialization_test.py +0 -50
- test/stub_composition_test.py +0 -10
- test/stub_test.py +0 -361
- test/test_asgi_wrapper.py +0 -234
- test/token_flow_test.py +0 -18
- test/traceback_test.py +0 -135
- test/tunnel_test.py +0 -29
- test/utils_test.py +0 -88
- test/version_test.py +0 -14
- test/volume_test.py +0 -397
- test/watcher_test.py +0 -58
- test/webhook_test.py +0 -145
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/LICENSE +0 -0
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/WHEEL +0 -0
- {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")
|
File without changes
|
File without changes
|
File without changes
|