modAL 1.3.1__tar.gz → 1.3.1.dev1__tar.gz
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-1.3.1 → modal-1.3.1.dev1}/PKG-INFO +2 -3
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/__init__.py +0 -2
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/__main__.py +1 -2
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_billing.py +5 -9
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_container_entrypoint.py +1 -13
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_functions.py +2 -5
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_grpc_client.py +3 -3
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_load_context.py +2 -26
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_object.py +11 -16
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_output.py +2 -2
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_partial_function.py +22 -1
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_resolver.py +31 -34
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_runtime/user_code_imports.py +63 -131
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_traceback.py +12 -3
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/async_utils.py +22 -51
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/blob_utils.py +9 -3
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/function_utils.py +4 -3
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/grpc_utils.py +9 -7
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/task_command_router_client.py +38 -25
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/app.py +14 -159
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/app.pyi +3 -121
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/_traceback.py +1 -2
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/container.py +2 -5
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/dict.py +3 -4
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/import_refs.py +2 -2
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/queues.py +3 -4
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/run.py +13 -65
- modal-1.3.1.dev1/modal/cli/token.py +60 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/client.py +0 -2
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/client.pyi +10 -5
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cls.py +3 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/experimental/__init__.py +1 -1
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/experimental/flash.py +7 -3
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/functions.pyi +6 -7
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/image.py +12 -37
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/image.pyi +0 -4
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/mount.py +4 -10
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/mount.pyi +3 -3
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/output.py +2 -2
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/partial_function.pyi +1 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/runner.py +12 -29
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/runner.pyi +1 -5
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/sandbox.py +2 -12
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/sandbox.pyi +0 -8
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/volume.pyi +2 -2
- {modal-1.3.1 → modal-1.3.1.dev1}/modal.egg-info/PKG-INFO +2 -3
- {modal-1.3.1 → modal-1.3.1.dev1}/modal.egg-info/SOURCES.txt +0 -3
- {modal-1.3.1 → modal-1.3.1.dev1}/modal.egg-info/requires.txt +1 -6
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/api.proto +1 -55
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/api_grpc.py +0 -16
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/api_pb2.py +656 -717
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/api_pb2.pyi +6 -175
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/api_pb2_grpc.py +0 -33
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/api_pb2_grpc.pyi +0 -10
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/modal_api_grpc.py +0 -1
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_version/__init__.py +2 -2
- {modal-1.3.1 → modal-1.3.1.dev1}/pyproject.toml +1 -2
- modal-1.3.1/modal/_server.py +0 -252
- modal-1.3.1/modal/cli/token.py +0 -108
- modal-1.3.1/modal/server.py +0 -5
- modal-1.3.1/modal/server.pyi +0 -147
- {modal-1.3.1 → modal-1.3.1.dev1}/LICENSE +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/README.md +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_clustered_functions.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_clustered_functions.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_ipython.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_location.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_pty.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_resources.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_runtime/__init__.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_runtime/asgi.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_runtime/execution_context.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_runtime/telemetry.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_runtime/user_code_event_loop.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_serialization.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_tunnel.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_tunnel.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_type_manager.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/__init__.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/app_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/deprecation.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/docker_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/git_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/hash_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/http_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/logger.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/mount_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/name_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/package_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/shell_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_utils/time_utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_vendor/__init__.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_vendor/tblib.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/_watcher.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/billing.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/builder/2023.12.txt +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/builder/2024.04.txt +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/builder/2024.10.txt +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/builder/2025.06.txt +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/builder/README.md +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/builder/base-images.json +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/call_graph.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/__init__.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/_download.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/app.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/cluster.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/config.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/entry_point.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/environment.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/launch.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/network_file_system.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/profile.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/programs/__init__.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/programs/vscode.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/secret.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/shell.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/utils.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cli/volume.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/cls.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/config.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/container_process.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/container_process.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/dict.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/dict.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/environments.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/environments.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/exception.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/experimental/flash.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/experimental/ipython.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/file_io.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/file_io.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/file_pattern_matcher.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/functions.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/gpu.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/io_streams.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/io_streams.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/network_file_system.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/network_file_system.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/object.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/object.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/parallel_map.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/parallel_map.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/partial_function.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/proxy.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/proxy.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/py.typed +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/queue.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/queue.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/retries.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/running_app.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/schedule.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/scheduler_placement.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/secret.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/secret.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/serving.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/serving.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/snapshot.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/snapshot.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/stream_type.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/token_flow.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/token_flow.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal/volume.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal.egg-info/top_level.txt +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_docs/__init__.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/__init__.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/py.typed +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/task_command_router.proto +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/task_command_router_grpc.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/task_command_router_pb2.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/task_command_router_pb2.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/task_command_router_pb2_grpc.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/modal_version/__main__.py +0 -0
- {modal-1.3.1 → modal-1.3.1.dev1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: modal
|
|
3
|
-
Version: 1.3.1
|
|
3
|
+
Version: 1.3.1.dev1
|
|
4
4
|
Summary: Python client library for Modal
|
|
5
5
|
Author-email: Modal Labs <support@modal.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -20,8 +20,7 @@ Requires-Dist: aiohttp
|
|
|
20
20
|
Requires-Dist: cbor2
|
|
21
21
|
Requires-Dist: certifi
|
|
22
22
|
Requires-Dist: click~=8.1
|
|
23
|
-
Requires-Dist: grpclib<0.4.10,>=0.4.7
|
|
24
|
-
Requires-Dist: grpclib<0.4.10,>=0.4.9; python_version >= "3.14"
|
|
23
|
+
Requires-Dist: grpclib<0.4.10,>=0.4.7
|
|
25
24
|
Requires-Dist: protobuf!=4.24.0,<7.0,>=3.19
|
|
26
25
|
Requires-Dist: rich>=12.0.0
|
|
27
26
|
Requires-Dist: synchronicity~=0.11.1
|
|
@@ -41,7 +41,6 @@ try:
|
|
|
41
41
|
from .schedule import Cron, Period
|
|
42
42
|
from .scheduler_placement import SchedulerPlacement
|
|
43
43
|
from .secret import Secret
|
|
44
|
-
from .server import Server
|
|
45
44
|
from .snapshot import SandboxSnapshot
|
|
46
45
|
from .volume import Volume
|
|
47
46
|
except Exception:
|
|
@@ -76,7 +75,6 @@ __all__ = [
|
|
|
76
75
|
"SandboxSnapshot",
|
|
77
76
|
"SchedulerPlacement",
|
|
78
77
|
"Secret",
|
|
79
|
-
"Server",
|
|
80
78
|
"Tunnel",
|
|
81
79
|
"Volume",
|
|
82
80
|
"asgi_app",
|
|
@@ -35,7 +35,6 @@ def main():
|
|
|
35
35
|
):
|
|
36
36
|
raise
|
|
37
37
|
|
|
38
|
-
from rich.markup import escape
|
|
39
38
|
from rich.panel import Panel
|
|
40
39
|
|
|
41
40
|
title = "Error"
|
|
@@ -44,7 +43,7 @@ def main():
|
|
|
44
43
|
content = f"{content}\n\nNote: {' '.join(notes)}"
|
|
45
44
|
|
|
46
45
|
console = make_console(stderr=True)
|
|
47
|
-
panel = Panel(
|
|
46
|
+
panel = Panel(content, title=title, title_align="left", border_style="red")
|
|
48
47
|
console.print(panel, highlight=False)
|
|
49
48
|
sys.exit(1)
|
|
50
49
|
|
|
@@ -52,15 +52,11 @@ async def _workspace_billing_report(
|
|
|
52
52
|
if end is None:
|
|
53
53
|
end = datetime.now(timezone.utc)
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if end.tzinfo is None:
|
|
61
|
-
end = end.replace(tzinfo=timezone.utc)
|
|
62
|
-
elif end.tzinfo != timezone.utc:
|
|
63
|
-
raise InvalidError("Timezone-aware 'end' parameter must be in UTC.")
|
|
55
|
+
for dt in (start, end):
|
|
56
|
+
if dt.tzinfo is None:
|
|
57
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
58
|
+
elif dt.tzinfo != timezone.utc:
|
|
59
|
+
raise InvalidError("Timezone-aware start/end limits must be in UTC.")
|
|
64
60
|
|
|
65
61
|
request = api_pb2.WorkspaceBillingReportRequest(
|
|
66
62
|
resolution=resolution,
|
|
@@ -5,7 +5,6 @@ import os
|
|
|
5
5
|
from modal._runtime.user_code_imports import (
|
|
6
6
|
Service,
|
|
7
7
|
import_class_service,
|
|
8
|
-
import_server_service,
|
|
9
8
|
import_single_function_service,
|
|
10
9
|
)
|
|
11
10
|
|
|
@@ -301,18 +300,7 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
|
301
300
|
param_kwargs = {}
|
|
302
301
|
|
|
303
302
|
with execution_context._import_context():
|
|
304
|
-
if function_def.
|
|
305
|
-
service_base_function_id = container_args.app_layout.function_ids[function_def.function_name]
|
|
306
|
-
service_function_hydration_data = [
|
|
307
|
-
o for o in container_args.app_layout.objects if o.object_id == service_base_function_id
|
|
308
|
-
][0]
|
|
309
|
-
service = import_server_service(
|
|
310
|
-
function_def,
|
|
311
|
-
service_function_hydration_data,
|
|
312
|
-
client,
|
|
313
|
-
ser_usr_cls,
|
|
314
|
-
)
|
|
315
|
-
elif function_def.is_class:
|
|
303
|
+
if function_def.is_class:
|
|
316
304
|
# this is a bit ugly - match the function and class based on function name to get metadata
|
|
317
305
|
# This metadata is required in order to hydrate the class in case it's not globally
|
|
318
306
|
# decorated (or serialized)
|
|
@@ -689,7 +689,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
689
689
|
nonpreemptible: bool = False,
|
|
690
690
|
is_builder_function: bool = False,
|
|
691
691
|
is_auto_snapshot: bool = False,
|
|
692
|
-
is_server: bool = False,
|
|
693
692
|
enable_memory_snapshot: bool = False,
|
|
694
693
|
block_network: bool = False,
|
|
695
694
|
restrict_modal_access: bool = False,
|
|
@@ -723,6 +722,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
723
722
|
f"Function {raw_f} has a schedule, so it needs to support being called with no arguments"
|
|
724
723
|
)
|
|
725
724
|
else:
|
|
725
|
+
# must be a "class service function"
|
|
726
726
|
assert info.user_cls
|
|
727
727
|
assert not webhook_config
|
|
728
728
|
assert not schedule
|
|
@@ -734,8 +734,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
734
734
|
]
|
|
735
735
|
|
|
736
736
|
retry_policy = _parse_retries(
|
|
737
|
-
retries,
|
|
738
|
-
f"Function '{info.get_tag()}'" if info.raw_f else f"Class '{info.get_tag()}'",
|
|
737
|
+
retries, f"Function '{info.get_tag()}'" if info.raw_f else f"Class '{info.get_tag()}'"
|
|
739
738
|
)
|
|
740
739
|
|
|
741
740
|
if retry_policy is not None:
|
|
@@ -1055,7 +1054,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1055
1054
|
supported_input_formats=supported_input_formats,
|
|
1056
1055
|
supported_output_formats=supported_output_formats,
|
|
1057
1056
|
http_config=http_config,
|
|
1058
|
-
is_server=is_server,
|
|
1059
1057
|
)
|
|
1060
1058
|
|
|
1061
1059
|
if isinstance(gpu, list):
|
|
@@ -1095,7 +1093,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1095
1093
|
supported_input_formats=supported_input_formats,
|
|
1096
1094
|
supported_output_formats=supported_output_formats,
|
|
1097
1095
|
http_config=http_config,
|
|
1098
|
-
is_server=function_definition.is_server,
|
|
1099
1096
|
)
|
|
1100
1097
|
|
|
1101
1098
|
ranked_functions = []
|
|
@@ -6,7 +6,7 @@ from google.protobuf.message import Message
|
|
|
6
6
|
from grpclib import GRPCError, Status
|
|
7
7
|
|
|
8
8
|
from . import exception
|
|
9
|
-
from ._traceback import
|
|
9
|
+
from ._traceback import suppress_tb_frames
|
|
10
10
|
from ._utils.grpc_utils import Retry, _retry_transient_errors
|
|
11
11
|
from .config import config, logger
|
|
12
12
|
|
|
@@ -50,7 +50,7 @@ class grpc_error_converter:
|
|
|
50
50
|
def __exit__(self, exc_type, exc, traceback) -> Literal[False]:
|
|
51
51
|
# skip all internal frames from grpclib
|
|
52
52
|
use_full_traceback = config.get("traceback")
|
|
53
|
-
with
|
|
53
|
+
with suppress_tb_frames(1):
|
|
54
54
|
if isinstance(exc, GRPCError):
|
|
55
55
|
modal_exc = _STATUS_TO_EXCEPTION[exc.status](exc.message)
|
|
56
56
|
modal_exc._grpc_message = exc.message
|
|
@@ -115,7 +115,7 @@ class UnaryUnaryWrapper(Generic[RequestType, ResponseType]):
|
|
|
115
115
|
timeout: Optional[float] = None,
|
|
116
116
|
metadata: Optional[list[tuple[str, str]]] = None,
|
|
117
117
|
) -> ResponseType:
|
|
118
|
-
with
|
|
118
|
+
with suppress_tb_frames(1):
|
|
119
119
|
if timeout is not None and retry is not None:
|
|
120
120
|
raise exception.InvalidError("Retry must be None when timeout is set")
|
|
121
121
|
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
# Copyright Modal Labs 2025
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional
|
|
3
3
|
|
|
4
4
|
from .client import _Client
|
|
5
5
|
from .config import config
|
|
6
6
|
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from ._utils.async_utils import TaskContext
|
|
9
|
-
|
|
10
7
|
|
|
11
8
|
class LoadContext:
|
|
12
9
|
"""Encapsulates optional metadata values used during object loading.
|
|
@@ -18,7 +15,6 @@ class LoadContext:
|
|
|
18
15
|
_client: Optional[_Client] = None
|
|
19
16
|
_environment_name: Optional[str] = None
|
|
20
17
|
_app_id: Optional[str] = None
|
|
21
|
-
_task_context: Optional["TaskContext"] = None
|
|
22
18
|
|
|
23
19
|
def __init__(
|
|
24
20
|
self,
|
|
@@ -26,12 +22,10 @@ class LoadContext:
|
|
|
26
22
|
client: Optional[_Client] = None,
|
|
27
23
|
environment_name: Optional[str] = None,
|
|
28
24
|
app_id: Optional[str] = None,
|
|
29
|
-
task_context: Optional["TaskContext"] = None,
|
|
30
25
|
):
|
|
31
26
|
self._client = client
|
|
32
27
|
self._environment_name = environment_name
|
|
33
28
|
self._app_id = app_id
|
|
34
|
-
self._task_context = task_context
|
|
35
29
|
|
|
36
30
|
@property
|
|
37
31
|
def client(self) -> _Client:
|
|
@@ -47,11 +41,6 @@ class LoadContext:
|
|
|
47
41
|
def app_id(self) -> Optional[str]:
|
|
48
42
|
return self._app_id
|
|
49
43
|
|
|
50
|
-
@property
|
|
51
|
-
def task_context(self) -> "TaskContext":
|
|
52
|
-
assert self._task_context is not None, "LoadContext has no TaskContext"
|
|
53
|
-
return self._task_context
|
|
54
|
-
|
|
55
44
|
@classmethod
|
|
56
45
|
def empty(cls) -> "LoadContext":
|
|
57
46
|
"""Create an empty LoadContext with all fields set to None.
|
|
@@ -70,7 +59,6 @@ class LoadContext:
|
|
|
70
59
|
client=self._client if self._client is not None else parent._client,
|
|
71
60
|
environment_name=self._environment_name if self._environment_name is not None else parent._environment_name,
|
|
72
61
|
app_id=self._app_id if self._app_id is not None else parent._app_id,
|
|
73
|
-
task_context=self._task_context if self._task_context is not None else parent._task_context,
|
|
74
62
|
) # TODO (elias): apply_defaults?
|
|
75
63
|
|
|
76
64
|
async def apply_defaults(self) -> "LoadContext":
|
|
@@ -83,27 +71,16 @@ class LoadContext:
|
|
|
83
71
|
client=self.client if is_valid_client else await _Client.from_env(),
|
|
84
72
|
environment_name=self._environment_name or config.get("environment") or "",
|
|
85
73
|
app_id=self._app_id,
|
|
86
|
-
task_context=self._task_context,
|
|
87
74
|
)
|
|
88
75
|
|
|
89
76
|
def reset(self) -> "LoadContext":
|
|
90
|
-
"""In-place replace all values with None, such that any inferred values/upgrades
|
|
91
|
-
will work. This is useful in cases where a load context reference may have leaked
|
|
92
|
-
into objects and used/upgraded but you want to make a fresh re-load, e.g. when doing
|
|
93
|
-
multiple `app.run()` calls in the same interpreter session.
|
|
94
|
-
"""
|
|
95
77
|
self._client = None
|
|
96
78
|
self._environment_name = None
|
|
97
79
|
self._app_id = None
|
|
98
|
-
self._task_context = None
|
|
99
80
|
return self
|
|
100
81
|
|
|
101
82
|
async def in_place_upgrade(
|
|
102
|
-
self,
|
|
103
|
-
client: Optional[_Client] = None,
|
|
104
|
-
environment_name: Optional[str] = None,
|
|
105
|
-
app_id: Optional[str] = None,
|
|
106
|
-
task_context: Optional["TaskContext"] = None,
|
|
83
|
+
self, client: Optional[_Client] = None, environment_name: Optional[str] = None, app_id: Optional[str] = None
|
|
107
84
|
) -> "LoadContext":
|
|
108
85
|
"""In-place set values if they aren't already set, or set default values
|
|
109
86
|
|
|
@@ -126,5 +103,4 @@ class LoadContext:
|
|
|
126
103
|
self._client = self._client or client or await _Client.from_env()
|
|
127
104
|
self._environment_name = self._environment_name or environment_name or config.get("environment") or ""
|
|
128
105
|
self._app_id = self._app_id or app_id
|
|
129
|
-
self._task_context = self._task_context or task_context
|
|
130
106
|
return self
|
|
@@ -9,11 +9,11 @@ from typing import Callable, ClassVar, Optional
|
|
|
9
9
|
from google.protobuf.message import Message
|
|
10
10
|
from typing_extensions import Self
|
|
11
11
|
|
|
12
|
-
from modal._traceback import
|
|
12
|
+
from modal._traceback import suppress_tb_frames
|
|
13
13
|
|
|
14
14
|
from ._load_context import LoadContext
|
|
15
15
|
from ._resolver import Resolver
|
|
16
|
-
from ._utils.async_utils import
|
|
16
|
+
from ._utils.async_utils import aclosing
|
|
17
17
|
from ._utils.deprecation import deprecation_warning
|
|
18
18
|
from .client import _Client
|
|
19
19
|
from .config import config, logger
|
|
@@ -127,11 +127,9 @@ class _Object:
|
|
|
127
127
|
assert isinstance(object_id, str) and self._type_prefix is not None
|
|
128
128
|
if not object_id.startswith(self._type_prefix):
|
|
129
129
|
raise ExecutionError(
|
|
130
|
-
f"Can not hydrate {type(self)}:
|
|
130
|
+
f"Can not hydrate {type(self)}:"
|
|
131
131
|
f" it has type prefix {self._type_prefix}"
|
|
132
|
-
f" but the object_id starts with {object_id[:3]}
|
|
133
|
-
"This usually means the object name was previously used for a different type. "
|
|
134
|
-
"Rename the object/app or stop the previous deployment and redeploy."
|
|
132
|
+
f" but the object_id starts with {object_id[:3]}"
|
|
135
133
|
)
|
|
136
134
|
self._object_id = object_id
|
|
137
135
|
self._client = client
|
|
@@ -139,7 +137,7 @@ class _Object:
|
|
|
139
137
|
self._is_hydrated = True
|
|
140
138
|
|
|
141
139
|
def _hydrate_metadata(self, metadata: Optional[Message]):
|
|
142
|
-
# override this
|
|
140
|
+
# override this is subclasses that need additional data (other than an object_id) for a functioning Handle
|
|
143
141
|
pass
|
|
144
142
|
|
|
145
143
|
def _get_metadata(self) -> Optional[Message]:
|
|
@@ -316,10 +314,9 @@ class _Object:
|
|
|
316
314
|
self._is_hydrated = False # un-hydrate and re-resolve
|
|
317
315
|
# we don't set an explicit Client here, relying on the default
|
|
318
316
|
# env client to be applied by LoadContext.apply_default
|
|
317
|
+
root_load_context = LoadContext.empty()
|
|
319
318
|
resolver = Resolver()
|
|
320
|
-
|
|
321
|
-
root_load_context = LoadContext(task_context=tc)
|
|
322
|
-
await resolver.load(typing.cast(_Object, self), root_load_context)
|
|
319
|
+
await resolver.load(typing.cast(_Object, self), root_load_context)
|
|
323
320
|
else:
|
|
324
321
|
logger.debug(f"reloading non-lazy {self} by replacing client")
|
|
325
322
|
self._client = client or await _Client.from_env()
|
|
@@ -328,13 +325,11 @@ class _Object:
|
|
|
328
325
|
elif not self._hydrate_lazily:
|
|
329
326
|
self._validate_is_hydrated()
|
|
330
327
|
else:
|
|
331
|
-
# Set the client on LoadContext before loading
|
|
332
|
-
|
|
328
|
+
# Set the client on LoadContext before loading
|
|
329
|
+
root_load_context = LoadContext(client=client)
|
|
333
330
|
resolver = Resolver()
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
with suppress_tb_frame(): # skip this frame by default
|
|
337
|
-
await resolver.load(self, root_load_context)
|
|
331
|
+
with suppress_tb_frames(1): # skip this frame by default
|
|
332
|
+
await resolver.load(self, root_load_context)
|
|
338
333
|
return self
|
|
339
334
|
|
|
340
335
|
|
|
@@ -169,9 +169,9 @@ class OutputManager:
|
|
|
169
169
|
|
|
170
170
|
@classmethod
|
|
171
171
|
@contextlib.contextmanager
|
|
172
|
-
def enable_output(cls, show_progress: bool = True
|
|
172
|
+
def enable_output(cls, show_progress: bool = True) -> Generator[None]:
|
|
173
173
|
if show_progress:
|
|
174
|
-
cls._instance = OutputManager(
|
|
174
|
+
cls._instance = OutputManager()
|
|
175
175
|
try:
|
|
176
176
|
yield
|
|
177
177
|
finally:
|
|
@@ -19,7 +19,7 @@ from ._functions import _Function
|
|
|
19
19
|
from ._utils.async_utils import synchronizer
|
|
20
20
|
from ._utils.deprecation import deprecation_warning
|
|
21
21
|
from ._utils.function_utils import callable_has_non_self_params
|
|
22
|
-
from .config import config
|
|
22
|
+
from .config import config, logger
|
|
23
23
|
from .exception import InvalidError
|
|
24
24
|
|
|
25
25
|
MAX_MAX_BATCH_SIZE = 1000
|
|
@@ -125,6 +125,7 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
|
125
125
|
user_cls: Optional[type] = None # class
|
|
126
126
|
flags: _PartialFunctionFlags
|
|
127
127
|
params: _PartialFunctionParams
|
|
128
|
+
registered: bool
|
|
128
129
|
|
|
129
130
|
def __init__(
|
|
130
131
|
self,
|
|
@@ -140,6 +141,7 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
|
140
141
|
self.user_cls = None
|
|
141
142
|
self.flags = flags
|
|
142
143
|
self.params = params
|
|
144
|
+
self.registered = False
|
|
143
145
|
self.validate_flag_composition()
|
|
144
146
|
|
|
145
147
|
def stack(self, flags: _PartialFunctionFlags, params: _PartialFunctionParams) -> typing_extensions.Self:
|
|
@@ -154,11 +156,13 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
|
154
156
|
uses_interface_flags = self.flags & _PartialFunctionFlags.interface_flags()
|
|
155
157
|
uses_lifecycle_flags = self.flags & _PartialFunctionFlags.lifecycle_flags()
|
|
156
158
|
if uses_interface_flags and uses_lifecycle_flags:
|
|
159
|
+
self.registered = True # Hacky, avoid false-positive warning
|
|
157
160
|
raise InvalidError("Interface decorators cannot be combined with lifecycle decorators.")
|
|
158
161
|
|
|
159
162
|
has_web_interface = self.flags & _PartialFunctionFlags.WEB_INTERFACE
|
|
160
163
|
has_callable_interface = self.flags & _PartialFunctionFlags.CALLABLE_INTERFACE
|
|
161
164
|
if has_web_interface and has_callable_interface:
|
|
165
|
+
self.registered = True # Hacky, avoid false-positive warning
|
|
162
166
|
raise InvalidError("Callable decorators cannot be combined with web interface decorators.")
|
|
163
167
|
|
|
164
168
|
def validate_obj_compatibility(
|
|
@@ -170,17 +174,20 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
|
170
174
|
uses_lifecycle_flags = self.flags & _PartialFunctionFlags.lifecycle_flags()
|
|
171
175
|
uses_interface_flags = self.flags & _PartialFunctionFlags.interface_flags()
|
|
172
176
|
if self.user_cls is not None and (uses_lifecycle_flags or uses_interface_flags):
|
|
177
|
+
self.registered = True # Hacky, avoid false-positive warning
|
|
173
178
|
raise InvalidError(
|
|
174
179
|
f"Cannot apply `@modal.{decorator_name}` to a class. Hint: consider applying to a method instead."
|
|
175
180
|
)
|
|
176
181
|
|
|
177
182
|
wrapped_object = self.raw_f or self.user_cls
|
|
178
183
|
if isinstance(wrapped_object, _Function):
|
|
184
|
+
self.registered = True # Hacky, avoid false-positive warning
|
|
179
185
|
raise InvalidError(
|
|
180
186
|
f"Cannot stack `@modal.{decorator_name}` on top of `@app.function`."
|
|
181
187
|
" Hint: swap the order of the decorators."
|
|
182
188
|
)
|
|
183
189
|
elif isinstance(wrapped_object, _Cls):
|
|
190
|
+
self.registered = True # Hacky, avoid false-positive warning
|
|
184
191
|
raise InvalidError(
|
|
185
192
|
f"Cannot stack `@modal.{decorator_name}` on top of `@app.cls()`."
|
|
186
193
|
" Hint: swap the order of the decorators."
|
|
@@ -189,12 +196,15 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
|
189
196
|
# Run some assertions about a callable wrappee defined by the specific decorator used
|
|
190
197
|
if self.raw_f is not None:
|
|
191
198
|
if not callable(self.raw_f):
|
|
199
|
+
self.registered = True # Hacky, avoid false-positive warning
|
|
192
200
|
raise InvalidError(f"The object wrapped by `@modal.{decorator_name}` must be callable.")
|
|
193
201
|
|
|
194
202
|
if require_sync and inspect.iscoroutinefunction(self.raw_f):
|
|
203
|
+
self.registered = True # Hacky, avoid false-positive warning
|
|
195
204
|
raise InvalidError(f"The `@modal.{decorator_name}` decorator can't be applied to an async function.")
|
|
196
205
|
|
|
197
206
|
if require_nullary and callable_has_non_self_params(self.raw_f):
|
|
207
|
+
self.registered = True # Hacky, avoid false-positive warning
|
|
198
208
|
raise InvalidError(f"Functions decorated by `@modal.{decorator_name}` can't have parameters.")
|
|
199
209
|
|
|
200
210
|
def _get_raw_f(self) -> Callable[P, ReturnType]:
|
|
@@ -234,6 +244,17 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
|
234
244
|
# to let it be bound to a variable and become a Function later on
|
|
235
245
|
return self # type: ignore # this returns a PartialFunction in a special internal case
|
|
236
246
|
|
|
247
|
+
def __del__(self):
|
|
248
|
+
if self.registered is False:
|
|
249
|
+
if self.raw_f is not None:
|
|
250
|
+
name, object_type, suggestion = self.raw_f.__name__, "function", "@app.function or @app.cls"
|
|
251
|
+
elif self.user_cls is not None:
|
|
252
|
+
name, object_type, suggestion = self.user_cls.__name__, "class", "@app.cls"
|
|
253
|
+
logger.warning(
|
|
254
|
+
f"The `{name}` {object_type} was never registered with the App."
|
|
255
|
+
f" Did you forget an {suggestion} decorator?"
|
|
256
|
+
)
|
|
257
|
+
|
|
237
258
|
|
|
238
259
|
def _find_partial_methods_for_user_cls(user_cls: type[Any], flags: int) -> dict[str, _PartialFunction]:
|
|
239
260
|
"""Grabs all method on a user class, and returns partials. Includes legacy methods."""
|
|
@@ -9,10 +9,11 @@ from collections.abc import Hashable
|
|
|
9
9
|
from typing import TYPE_CHECKING, Optional
|
|
10
10
|
|
|
11
11
|
import modal._object
|
|
12
|
-
from modal._traceback import
|
|
12
|
+
from modal._traceback import suppress_tb_frames
|
|
13
13
|
from modal_proto import api_pb2
|
|
14
14
|
|
|
15
15
|
from ._load_context import LoadContext
|
|
16
|
+
from ._utils.async_utils import TaskContext
|
|
16
17
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
18
19
|
from rich.tree import Tree
|
|
@@ -34,7 +35,7 @@ class StatusRow:
|
|
|
34
35
|
|
|
35
36
|
def warning(self, warning: api_pb2.Warning):
|
|
36
37
|
if self._step_node is not None:
|
|
37
|
-
self._step_node.add(f"
|
|
38
|
+
self._step_node.add(f"[yellow]:warning:[/yellow] {warning.message}")
|
|
38
39
|
|
|
39
40
|
def finish(self, message):
|
|
40
41
|
if self._step_node is not None and self._spinner is not None:
|
|
@@ -119,41 +120,37 @@ class Resolver:
|
|
|
119
120
|
if not cached_future:
|
|
120
121
|
# don't run any awaits within this if-block to prevent race conditions
|
|
121
122
|
async def loader():
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return obj
|
|
150
|
-
|
|
151
|
-
# use task_context from load_context to make sure tasks are cleaned up eventually
|
|
152
|
-
cached_future = parent_load_context.task_context.create_task(loader())
|
|
123
|
+
load_context = await obj._load_context_overrides.merged_with(parent_load_context).apply_defaults()
|
|
124
|
+
|
|
125
|
+
# TODO(erikbern): do we need existing_object_id for those?
|
|
126
|
+
await TaskContext.gather(*[self.load(dep, load_context) for dep in obj.deps()])
|
|
127
|
+
|
|
128
|
+
# Load the object itself
|
|
129
|
+
if not obj._load:
|
|
130
|
+
raise Exception(f"Object {obj} has no loader function")
|
|
131
|
+
|
|
132
|
+
await obj._load(obj, self, load_context, existing_object_id)
|
|
133
|
+
|
|
134
|
+
# Check that the id of functions didn't change
|
|
135
|
+
# Persisted refs are ignored because their life cycle is managed independently.
|
|
136
|
+
if (
|
|
137
|
+
not obj._is_another_app
|
|
138
|
+
and existing_object_id is not None
|
|
139
|
+
and existing_object_id.startswith("fu-")
|
|
140
|
+
and obj.object_id != existing_object_id
|
|
141
|
+
):
|
|
142
|
+
raise Exception(
|
|
143
|
+
f"Tried creating an object using existing id {existing_object_id} but it has id {obj.object_id}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
return obj
|
|
147
|
+
|
|
148
|
+
cached_future = asyncio.create_task(loader())
|
|
153
149
|
self._local_uuid_to_future[obj.local_uuid] = cached_future
|
|
154
150
|
if deduplication_key is not None:
|
|
155
151
|
self._deduplication_cache[deduplication_key] = cached_future
|
|
156
|
-
with
|
|
152
|
+
with suppress_tb_frames(2):
|
|
153
|
+
# skip current frame + `loader()` closure frame from above
|
|
157
154
|
return await cached_future
|
|
158
155
|
|
|
159
156
|
def objects(self) -> list["modal._object._Object"]:
|