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
modal/runner.pyi CHANGED
@@ -1,7 +1,8 @@
1
- import modal._output
2
1
  import modal.client
3
- import modal.object
2
+ import modal.cls
3
+ import modal.functions
4
4
  import modal.running_app
5
+ import modal_proto.api_pb2
5
6
  import multiprocessing.synchronize
6
7
  import synchronicity.combined_types
7
8
  import typing
@@ -9,131 +10,144 @@ import typing_extensions
9
10
 
10
11
  _App = typing.TypeVar("_App")
11
12
 
12
- async def _heartbeat(client, app_id):
13
- ...
14
-
15
-
16
- async def _init_local_app_existing(client: modal.client._Client, existing_app_id: str) -> modal.running_app.RunningApp:
17
- ...
18
-
19
-
20
- async def _init_local_app_new(client: modal.client._Client, description: str, app_state: int, environment_name: str = '', interactive=False) -> modal.running_app.RunningApp:
21
- ...
22
-
23
-
24
- async def _init_local_app_from_name(client: modal.client._Client, name: str, namespace, environment_name: str = '') -> modal.running_app.RunningApp:
25
- ...
26
-
27
-
28
- async def _create_all_objects(client: modal.client._Client, running_app: modal.running_app.RunningApp, indexed_objects: typing.Dict[str, modal.object._Object], new_app_state: int, environment_name: str, output_mgr: typing.Union[modal._output.OutputManager, None] = None):
29
- ...
30
-
31
-
32
- async def _disconnect(client: modal.client._Client, app_id: str, reason: typing.Union[int, None] = None, exc_str: typing.Union[str, None] = None):
33
- ...
34
-
35
-
36
- def _run_app(app: _App, client: typing.Union[modal.client._Client, None] = None, stdout=None, show_progress: bool = True, detach: bool = False, output_mgr: typing.Union[modal._output.OutputManager, None] = None, environment_name: typing.Union[str, None] = None, shell=False, interactive=False) -> typing.AsyncContextManager[_App]:
37
- ...
38
-
39
-
40
- async def _serve_update(app: _App, existing_app_id: str, is_ready: multiprocessing.synchronize.Event, environment_name: str) -> None:
41
- ...
42
-
13
+ V = typing.TypeVar("V")
14
+
15
+ async def _heartbeat(client: modal.client._Client, app_id: str) -> None: ...
16
+ async def _init_local_app_existing(
17
+ client: modal.client._Client, existing_app_id: str, environment_name: str
18
+ ) -> modal.running_app.RunningApp: ...
19
+ async def _init_local_app_new(
20
+ client: modal.client._Client,
21
+ description: str,
22
+ app_state: int,
23
+ environment_name: str = "",
24
+ interactive: bool = False,
25
+ ) -> modal.running_app.RunningApp: ...
26
+ async def _init_local_app_from_name(
27
+ client: modal.client._Client, name: str, namespace: typing.Any, environment_name: str = ""
28
+ ) -> modal.running_app.RunningApp: ...
29
+ async def _create_all_objects(
30
+ client: modal.client._Client,
31
+ running_app: modal.running_app.RunningApp,
32
+ functions: dict[str, modal.functions._Function],
33
+ classes: dict[str, modal.cls._Cls],
34
+ environment_name: str,
35
+ ) -> None: ...
36
+ async def _publish_app(
37
+ client: modal.client._Client,
38
+ running_app: modal.running_app.RunningApp,
39
+ app_state: int,
40
+ functions: dict[str, modal.functions._Function],
41
+ classes: dict[str, modal.cls._Cls],
42
+ name: str = "",
43
+ tag: str = "",
44
+ ) -> tuple[str, list[modal_proto.api_pb2.Warning]]: ...
45
+ async def _disconnect(client: modal.client._Client, app_id: str, reason: int, exc_str: str = "") -> None: ...
46
+ async def _status_based_disconnect(
47
+ client: modal.client._Client, app_id: str, exc_info: typing.Optional[BaseException] = None
48
+ ): ...
49
+ def _run_app(
50
+ app: _App,
51
+ *,
52
+ client: typing.Optional[modal.client._Client] = None,
53
+ detach: bool = False,
54
+ environment_name: typing.Optional[str] = None,
55
+ interactive: bool = False,
56
+ ) -> typing.AsyncContextManager[_App]: ...
57
+ async def _serve_update(
58
+ app: _App, existing_app_id: str, is_ready: multiprocessing.synchronize.Event, environment_name: str
59
+ ) -> None: ...
43
60
 
44
61
  class DeployResult:
45
62
  app_id: str
46
-
47
- def __init__(self, app_id: str) -> None:
48
- ...
49
-
50
- def __repr__(self):
51
- ...
52
-
53
- def __eq__(self, other):
54
- ...
55
-
56
- def __setattr__(self, name, value):
57
- ...
58
-
59
- def __delattr__(self, name):
60
- ...
61
-
62
- def __hash__(self):
63
- ...
64
-
65
-
66
- async def _deploy_app(app: _App, name: str = None, namespace=1, client=None, stdout=None, show_progress=True, environment_name: typing.Union[str, None] = None, public: bool = False) -> DeployResult:
67
- ...
68
-
69
-
70
- async def _interactive_shell(_app: _App, cmd: typing.List[str], environment_name: str = '', **kwargs):
71
- ...
72
-
63
+ app_page_url: str
64
+ app_logs_url: str
65
+ warnings: list[str]
66
+
67
+ def __init__(self, app_id: str, app_page_url: str, app_logs_url: str, warnings: list[str]) -> None: ...
68
+ def __repr__(self): ...
69
+ def __eq__(self, other): ...
70
+ def __setattr__(self, name, value): ...
71
+ def __delattr__(self, name): ...
72
+ def __hash__(self): ...
73
+
74
+ async def _deploy_app(
75
+ app: _App,
76
+ name: typing.Optional[str] = None,
77
+ namespace: typing.Any = 1,
78
+ client: typing.Optional[modal.client._Client] = None,
79
+ environment_name: typing.Optional[str] = None,
80
+ tag: str = "",
81
+ ) -> DeployResult: ...
82
+ async def _interactive_shell(
83
+ _app: _App, cmds: list[str], environment_name: str = "", pty: bool = True, **kwargs: typing.Any
84
+ ) -> None: ...
85
+ def _run_stub(*args: typing.Any, **kwargs: typing.Any): ...
86
+ def _deploy_stub(*args: typing.Any, **kwargs: typing.Any): ...
73
87
 
74
88
  class __run_app_spec(typing_extensions.Protocol):
75
- def __call__(self, app: _App, client: typing.Union[modal.client.Client, None] = None, stdout=None, show_progress: bool = True, detach: bool = False, output_mgr: typing.Union[modal._output.OutputManager, None] = None, environment_name: typing.Union[str, None] = None, shell=False, interactive=False) -> synchronicity.combined_types.AsyncAndBlockingContextManager[_App]:
76
- ...
77
-
78
- def aio(self, app: _App, client: typing.Union[modal.client.Client, None] = None, stdout=None, show_progress: bool = True, detach: bool = False, output_mgr: typing.Union[modal._output.OutputManager, None] = None, environment_name: typing.Union[str, None] = None, shell=False, interactive=False) -> typing.AsyncContextManager[_App]:
79
- ...
89
+ def __call__(
90
+ self,
91
+ app: _App,
92
+ *,
93
+ client: typing.Optional[modal.client.Client] = None,
94
+ detach: bool = False,
95
+ environment_name: typing.Optional[str] = None,
96
+ interactive: bool = False,
97
+ ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[_App]: ...
98
+ def aio(
99
+ self,
100
+ app: _App,
101
+ *,
102
+ client: typing.Optional[modal.client.Client] = None,
103
+ detach: bool = False,
104
+ environment_name: typing.Optional[str] = None,
105
+ interactive: bool = False,
106
+ ) -> typing.AsyncContextManager[_App]: ...
80
107
 
81
108
  run_app: __run_app_spec
82
109
 
83
-
84
110
  class __serve_update_spec(typing_extensions.Protocol):
85
- def __call__(self, app: _App, existing_app_id: str, is_ready: multiprocessing.synchronize.Event, environment_name: str) -> None:
86
- ...
87
-
88
- async def aio(self, *args, **kwargs) -> None:
89
- ...
111
+ def __call__(
112
+ self, app: _App, existing_app_id: str, is_ready: multiprocessing.synchronize.Event, environment_name: str
113
+ ) -> None: ...
114
+ async def aio(
115
+ self, app: _App, existing_app_id: str, is_ready: multiprocessing.synchronize.Event, environment_name: str
116
+ ) -> None: ...
90
117
 
91
118
  serve_update: __serve_update_spec
92
119
 
93
-
94
120
  class __deploy_app_spec(typing_extensions.Protocol):
95
- def __call__(self, app: _App, name: str = None, namespace=1, client=None, stdout=None, show_progress=True, environment_name: typing.Union[str, None] = None, public: bool = False) -> DeployResult:
96
- ...
97
-
98
- async def aio(self, *args, **kwargs) -> DeployResult:
99
- ...
121
+ def __call__(
122
+ self,
123
+ app: _App,
124
+ name: typing.Optional[str] = None,
125
+ namespace: typing.Any = 1,
126
+ client: typing.Optional[modal.client.Client] = None,
127
+ environment_name: typing.Optional[str] = None,
128
+ tag: str = "",
129
+ ) -> DeployResult: ...
130
+ async def aio(
131
+ self,
132
+ app: _App,
133
+ name: typing.Optional[str] = None,
134
+ namespace: typing.Any = 1,
135
+ client: typing.Optional[modal.client.Client] = None,
136
+ environment_name: typing.Optional[str] = None,
137
+ tag: str = "",
138
+ ) -> DeployResult: ...
100
139
 
101
140
  deploy_app: __deploy_app_spec
102
141
 
103
-
104
142
  class __interactive_shell_spec(typing_extensions.Protocol):
105
- def __call__(self, _app: _App, cmd: typing.List[str], environment_name: str = '', **kwargs):
106
- ...
107
-
108
- async def aio(self, *args, **kwargs):
109
- ...
143
+ def __call__(
144
+ self, _app: _App, cmds: list[str], environment_name: str = "", pty: bool = True, **kwargs: typing.Any
145
+ ) -> None: ...
146
+ async def aio(
147
+ self, _app: _App, cmds: list[str], environment_name: str = "", pty: bool = True, **kwargs: typing.Any
148
+ ) -> None: ...
110
149
 
111
150
  interactive_shell: __interactive_shell_spec
112
151
 
113
-
114
- def _run_stub(app: _App, client: typing.Union[modal.client._Client, None] = None, stdout=None, show_progress: bool = True, detach: bool = False, output_mgr: typing.Union[modal._output.OutputManager, None] = None, environment_name: typing.Union[str, None] = None, shell=False, interactive=False) -> typing.AsyncContextManager[_App]:
115
- ...
116
-
117
-
118
- class __run_stub_spec(typing_extensions.Protocol):
119
- def __call__(self, app: _App, client: typing.Union[modal.client.Client, None] = None, stdout=None, show_progress: bool = True, detach: bool = False, output_mgr: typing.Union[modal._output.OutputManager, None] = None, environment_name: typing.Union[str, None] = None, shell=False, interactive=False) -> synchronicity.combined_types.AsyncAndBlockingContextManager[_App]:
120
- ...
121
-
122
- def aio(self, app: _App, client: typing.Union[modal.client.Client, None] = None, stdout=None, show_progress: bool = True, detach: bool = False, output_mgr: typing.Union[modal._output.OutputManager, None] = None, environment_name: typing.Union[str, None] = None, shell=False, interactive=False) -> typing.AsyncContextManager[_App]:
123
- ...
124
-
125
- run_stub: __run_stub_spec
126
-
127
-
128
- async def _deploy_stub(app: _App, name: str = None, namespace=1, client=None, stdout=None, show_progress=True, environment_name: typing.Union[str, None] = None, public: bool = False) -> DeployResult:
129
- ...
130
-
131
-
132
- class __deploy_stub_spec(typing_extensions.Protocol):
133
- def __call__(self, app: _App, name: str = None, namespace=1, client=None, stdout=None, show_progress=True, environment_name: typing.Union[str, None] = None, public: bool = False) -> DeployResult:
134
- ...
135
-
136
- async def aio(self, *args, **kwargs) -> DeployResult:
137
- ...
138
-
139
- deploy_stub: __deploy_stub_spec
152
+ def run_stub(*args: typing.Any, **kwargs: typing.Any): ...
153
+ def deploy_stub(*args: typing.Any, **kwargs: typing.Any): ...
modal/running_app.py CHANGED
@@ -1,15 +1,38 @@
1
1
  # Copyright Modal Labs 2024
2
2
  from dataclasses import dataclass, field
3
- from typing import Dict, Optional
3
+ from typing import Optional
4
4
 
5
5
  from google.protobuf.message import Message
6
6
 
7
+ from modal._utils.grpc_utils import get_proto_oneof
8
+ from modal_proto import api_pb2
9
+
7
10
 
8
11
  @dataclass
9
12
  class RunningApp:
10
13
  app_id: str
11
- environment_name: Optional[str] = None
12
14
  app_page_url: Optional[str] = None
13
- tag_to_object_id: Dict[str, str] = field(default_factory=dict)
14
- object_handle_metadata: Dict[str, Optional[Message]] = field(default_factory=dict)
15
+ app_logs_url: Optional[str] = None
16
+ function_ids: dict[str, str] = field(default_factory=dict)
17
+ class_ids: dict[str, str] = field(default_factory=dict)
18
+ object_handle_metadata: dict[str, Optional[Message]] = field(default_factory=dict)
15
19
  interactive: bool = False
20
+
21
+
22
+ def running_app_from_layout(
23
+ app_id: str,
24
+ app_layout: api_pb2.AppLayout,
25
+ app_page_url: Optional[str] = None,
26
+ ) -> RunningApp:
27
+ object_handle_metadata = {}
28
+ for obj in app_layout.objects:
29
+ handle_metadata: Optional[Message] = get_proto_oneof(obj, "handle_metadata_oneof")
30
+ object_handle_metadata[obj.object_id] = handle_metadata
31
+
32
+ return RunningApp(
33
+ app_id,
34
+ function_ids=dict(app_layout.function_ids),
35
+ class_ids=dict(app_layout.class_ids),
36
+ object_handle_metadata=object_handle_metadata,
37
+ app_page_url=app_page_url,
38
+ )