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/cpu_test.py
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# Copyright Modal Labs 2023
|
2
|
-
import pytest
|
3
|
-
|
4
|
-
from modal import App
|
5
|
-
from modal.exception import InvalidError
|
6
|
-
|
7
|
-
|
8
|
-
def dummy():
|
9
|
-
pass
|
10
|
-
|
11
|
-
|
12
|
-
def test_cpu_lower_bound(client, servicer):
|
13
|
-
app = App()
|
14
|
-
|
15
|
-
app.function(cpu=0.0)(dummy)
|
16
|
-
|
17
|
-
with pytest.raises(InvalidError):
|
18
|
-
with app.run(client=client):
|
19
|
-
pass
|
20
|
-
|
21
|
-
app.function(cpu=42)(dummy)
|
22
|
-
with app.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 App, asgi_app, method, web_endpoint, wsgi_app
|
5
|
-
from modal.exception import InvalidError
|
6
|
-
|
7
|
-
|
8
|
-
def test_local_entrypoint_forgot_parentheses():
|
9
|
-
app = App()
|
10
|
-
|
11
|
-
with pytest.raises(InvalidError, match="local_entrypoint()"):
|
12
|
-
|
13
|
-
@app.local_entrypoint # type: ignore
|
14
|
-
def f():
|
15
|
-
pass
|
16
|
-
|
17
|
-
|
18
|
-
def test_function_forgot_parentheses():
|
19
|
-
app = App()
|
20
|
-
|
21
|
-
with pytest.raises(InvalidError, match="function()"):
|
22
|
-
|
23
|
-
@app.function # type: ignore
|
24
|
-
def f():
|
25
|
-
pass
|
26
|
-
|
27
|
-
|
28
|
-
def test_cls_forgot_parentheses():
|
29
|
-
app = App()
|
30
|
-
|
31
|
-
with pytest.raises(InvalidError, match="cls()"):
|
32
|
-
|
33
|
-
@app.cls # type: ignore
|
34
|
-
class XYZ:
|
35
|
-
pass
|
36
|
-
|
37
|
-
|
38
|
-
def test_method_forgot_parentheses():
|
39
|
-
app = App()
|
40
|
-
|
41
|
-
with pytest.raises(InvalidError, match="method()"):
|
42
|
-
|
43
|
-
@app.cls()
|
44
|
-
class XYZ:
|
45
|
-
@method # type: ignore
|
46
|
-
def f(self):
|
47
|
-
pass
|
48
|
-
|
49
|
-
|
50
|
-
def test_invalid_web_decorator_usage():
|
51
|
-
app = App()
|
52
|
-
|
53
|
-
with pytest.raises(InvalidError, match="web_endpoint()"):
|
54
|
-
|
55
|
-
@app.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
|
-
@app.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
|
-
@app.function() # type: ignore
|
70
|
-
@wsgi_app # type: ignore
|
71
|
-
def my_handle_wsgi():
|
72
|
-
pass
|
73
|
-
|
74
|
-
|
75
|
-
def test_web_endpoint_method():
|
76
|
-
app = App()
|
77
|
-
|
78
|
-
with pytest.raises(InvalidError, match="remove the `@method`"):
|
79
|
-
|
80
|
-
@app.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,51 +0,0 @@
|
|
1
|
-
# Copyright Modal Labs 2022
|
2
|
-
import pytest
|
3
|
-
import time
|
4
|
-
|
5
|
-
from modal import Dict
|
6
|
-
from modal.exception import NotFoundError
|
7
|
-
|
8
|
-
|
9
|
-
def test_dict_app(servicer, client):
|
10
|
-
d = Dict.lookup("my-amazing-dict", {"xyz": 123}, create_if_missing=True, client=client)
|
11
|
-
d["foo"] = 42
|
12
|
-
d["foo"] += 5
|
13
|
-
assert d["foo"] == 47
|
14
|
-
assert d.len() == 2
|
15
|
-
|
16
|
-
assert sorted(d.keys()) == ["foo", "xyz"]
|
17
|
-
assert sorted(d.values()) == [47, 123]
|
18
|
-
assert sorted(d.items()) == [("foo", 47), ("xyz", 123)]
|
19
|
-
|
20
|
-
d.clear()
|
21
|
-
assert d.len() == 0
|
22
|
-
with pytest.raises(KeyError):
|
23
|
-
_ = d["foo"]
|
24
|
-
|
25
|
-
assert d.get("foo", default=True)
|
26
|
-
d["foo"] = None
|
27
|
-
assert d["foo"] is None
|
28
|
-
|
29
|
-
Dict.delete("my-amazing-dict", client=client)
|
30
|
-
with pytest.raises(NotFoundError):
|
31
|
-
Dict.lookup("my-amazing-dict", client=client)
|
32
|
-
|
33
|
-
|
34
|
-
def test_dict_ephemeral(servicer, client):
|
35
|
-
assert servicer.n_dict_heartbeats == 0
|
36
|
-
with Dict.ephemeral({"bar": 123}, client=client, _heartbeat_sleep=1) as d:
|
37
|
-
d["foo"] = 42
|
38
|
-
assert d.len() == 2
|
39
|
-
assert d["foo"] == 42
|
40
|
-
assert d["bar"] == 123
|
41
|
-
time.sleep(1.5) # Make time for 2 heartbeats
|
42
|
-
assert servicer.n_dict_heartbeats == 2
|
43
|
-
|
44
|
-
|
45
|
-
def test_dict_lazy_hydrate_named(set_env_client, servicer):
|
46
|
-
with servicer.intercept() as ctx:
|
47
|
-
d = Dict.from_name("foo", create_if_missing=True)
|
48
|
-
assert len(ctx.get_requests("DictGetOrCreate")) == 0 # sanity check that the get request is lazy
|
49
|
-
d["foo"] = 42
|
50
|
-
assert d["foo"] == 42
|
51
|
-
assert len(ctx.get_requests("DictGetOrCreate")) == 1 # just sanity check that object is only hydrated once...
|
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,32 +0,0 @@
|
|
1
|
-
# Copyright Modal Labs 2023
|
2
|
-
import pytest
|
3
|
-
|
4
|
-
from modal import App
|
5
|
-
from modal._serialization import deserialize
|
6
|
-
|
7
|
-
|
8
|
-
@pytest.mark.asyncio
|
9
|
-
async def test_serialize_deserialize_function(servicer, client):
|
10
|
-
app = App()
|
11
|
-
|
12
|
-
@app.function(serialized=True, name="foo")
|
13
|
-
def foo():
|
14
|
-
2 * foo.remote()
|
15
|
-
|
16
|
-
assert foo.object_id is None
|
17
|
-
|
18
|
-
with app.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
|