modal 0.62.16__py3-none-any.whl → 0.72.11__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 +17 -13
- modal/__main__.py +41 -3
- modal/_clustered_functions.py +80 -0
- modal/_clustered_functions.pyi +22 -0
- modal/_container_entrypoint.py +420 -937
- modal/_ipython.py +3 -13
- modal/_location.py +17 -10
- modal/_output.py +243 -99
- modal/_pty.py +2 -2
- modal/_resolver.py +55 -59
- modal/_resources.py +51 -0
- modal/_runtime/__init__.py +1 -0
- modal/_runtime/asgi.py +519 -0
- modal/_runtime/container_io_manager.py +1036 -0
- modal/_runtime/execution_context.py +89 -0
- modal/_runtime/telemetry.py +169 -0
- modal/_runtime/user_code_imports.py +356 -0
- modal/_serialization.py +134 -9
- modal/_traceback.py +47 -187
- modal/_tunnel.py +52 -16
- modal/_tunnel.pyi +19 -36
- modal/_utils/app_utils.py +3 -17
- modal/_utils/async_utils.py +479 -100
- 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 +460 -171
- modal/_utils/grpc_testing.py +47 -31
- modal/_utils/grpc_utils.py +62 -109
- modal/_utils/hash_utils.py +61 -19
- 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 +5 -7
- modal/_utils/shell_utils.py +15 -49
- modal/_vendor/a2wsgi_wsgi.py +62 -72
- modal/_vendor/cloudpickle.py +1 -1
- modal/_watcher.py +14 -12
- modal/app.py +1003 -314
- modal/app.pyi +540 -264
- modal/call_graph.py +7 -6
- modal/cli/_download.py +63 -53
- modal/cli/_traceback.py +200 -0
- modal/cli/app.py +205 -45
- modal/cli/config.py +12 -5
- modal/cli/container.py +62 -14
- modal/cli/dict.py +128 -0
- modal/cli/entry_point.py +26 -13
- modal/cli/environment.py +40 -9
- modal/cli/import_refs.py +64 -58
- modal/cli/launch.py +32 -18
- modal/cli/network_file_system.py +64 -83
- modal/cli/profile.py +1 -1
- modal/cli/programs/run_jupyter.py +35 -10
- modal/cli/programs/vscode.py +60 -10
- modal/cli/queues.py +131 -0
- modal/cli/run.py +234 -131
- modal/cli/secret.py +8 -7
- modal/cli/token.py +7 -2
- modal/cli/utils.py +79 -10
- modal/cli/volume.py +110 -109
- modal/client.py +250 -144
- modal/client.pyi +157 -118
- modal/cloud_bucket_mount.py +108 -34
- modal/cloud_bucket_mount.pyi +32 -38
- modal/cls.py +535 -148
- modal/cls.pyi +190 -146
- modal/config.py +41 -19
- modal/container_process.py +177 -0
- modal/container_process.pyi +82 -0
- modal/dict.py +111 -65
- modal/dict.pyi +136 -131
- modal/environments.py +106 -5
- modal/environments.pyi +77 -25
- modal/exception.py +34 -43
- modal/experimental.py +61 -2
- modal/extensions/ipython.py +5 -5
- modal/file_io.py +537 -0
- modal/file_io.pyi +235 -0
- modal/file_pattern_matcher.py +197 -0
- modal/functions.py +906 -911
- modal/functions.pyi +466 -430
- modal/gpu.py +57 -44
- modal/image.py +1089 -479
- modal/image.pyi +584 -228
- modal/io_streams.py +434 -0
- modal/io_streams.pyi +122 -0
- modal/mount.py +314 -101
- modal/mount.pyi +241 -235
- modal/network_file_system.py +92 -92
- modal/network_file_system.pyi +152 -110
- modal/object.py +67 -36
- modal/object.pyi +166 -143
- modal/output.py +63 -0
- modal/parallel_map.py +434 -0
- modal/parallel_map.pyi +75 -0
- modal/partial_function.py +282 -117
- modal/partial_function.pyi +222 -129
- modal/proxy.py +15 -12
- modal/proxy.pyi +3 -8
- modal/queue.py +182 -65
- modal/queue.pyi +218 -118
- modal/requirements/2024.04.txt +29 -0
- modal/requirements/2024.10.txt +16 -0
- modal/requirements/README.md +21 -0
- modal/requirements/base-images.json +22 -0
- modal/retries.py +48 -7
- modal/runner.py +459 -156
- modal/runner.pyi +135 -71
- modal/running_app.py +38 -0
- modal/sandbox.py +514 -236
- modal/sandbox.pyi +397 -169
- modal/schedule.py +4 -4
- modal/scheduler_placement.py +20 -3
- modal/secret.py +56 -31
- modal/secret.pyi +62 -42
- modal/serving.py +51 -56
- modal/serving.pyi +44 -36
- modal/stream_type.py +15 -0
- modal/token_flow.py +5 -3
- modal/token_flow.pyi +37 -32
- modal/volume.py +285 -157
- modal/volume.pyi +249 -184
- {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/METADATA +7 -7
- modal-0.72.11.dist-info/RECORD +174 -0
- {modal-0.62.16.dist-info → modal-0.72.11.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 +5 -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 +1288 -533
- modal_proto/api_grpc.py +856 -456
- modal_proto/api_pb2.py +2165 -1157
- modal_proto/api_pb2.pyi +8859 -0
- modal_proto/api_pb2_grpc.py +1674 -855
- 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_entrypoint.pyi +0 -378
- modal/_container_exec.py +0 -128
- modal/_sandbox_shell.py +0 -49
- modal/shared_volume.py +0 -23
- modal/shared_volume.pyi +0 -24
- modal/stub.py +0 -783
- modal/stub.pyi +0 -332
- modal-0.62.16.dist-info/RECORD +0 -198
- 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 -262
- test/blob_test.py +0 -67
- test/cli_imports_test.py +0 -149
- test/cli_test.py +0 -659
- test/client_test.py +0 -194
- test/cls_test.py +0 -630
- test/config_test.py +0 -137
- test/conftest.py +0 -1420
- test/container_app_test.py +0 -32
- test/container_test.py +0 -1389
- test/cpu_test.py +0 -23
- test/decorator_test.py +0 -85
- test/deprecation_test.py +0 -34
- test/dict_test.py +0 -33
- test/e2e_test.py +0 -68
- test/error_test.py +0 -7
- test/function_serialization_test.py +0 -32
- test/function_test.py +0 -653
- test/function_utils_test.py +0 -101
- test/gpu_test.py +0 -159
- test/grpc_utils_test.py +0 -141
- test/helpers.py +0 -42
- test/image_test.py +0 -669
- 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 -329
- test/network_file_system_test.py +0 -181
- test/notebook_test.py +0 -66
- test/object_test.py +0 -41
- test/package_utils_test.py +0 -25
- test/queue_test.py +0 -97
- test/resolver_test.py +0 -58
- 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 -29
- test/secret_test.py +0 -78
- test/serialization_test.py +0 -42
- test/stub_composition_test.py +0 -10
- test/stub_test.py +0 -360
- 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 -341
- test/watcher_test.py +0 -30
- test/webhook_test.py +0 -146
- /modal/{requirements.312.txt → requirements/2023.12.312.txt} +0 -0
- /modal/{requirements.txt → requirements/2023.12.txt} +0 -0
- {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/LICENSE +0 -0
- {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/WHEEL +0 -0
- {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/entry_points.txt +0 -0
modal/dict.pyi
CHANGED
@@ -1,195 +1,200 @@
|
|
1
|
+
import collections.abc
|
1
2
|
import modal.client
|
2
3
|
import modal.object
|
3
4
|
import synchronicity.combined_types
|
4
5
|
import typing
|
5
6
|
import typing_extensions
|
6
7
|
|
7
|
-
def _serialize_dict(data):
|
8
|
-
...
|
9
|
-
|
8
|
+
def _serialize_dict(data): ...
|
10
9
|
|
11
10
|
class _Dict(modal.object._Object):
|
12
|
-
|
13
|
-
def new(data: typing.Union[dict, None] = None) -> _Dict:
|
14
|
-
...
|
15
|
-
|
16
|
-
def __init__(self, data={}):
|
17
|
-
...
|
18
|
-
|
11
|
+
def __init__(self, data={}): ...
|
19
12
|
@classmethod
|
20
|
-
def ephemeral(
|
21
|
-
|
22
|
-
|
13
|
+
def ephemeral(
|
14
|
+
cls: type[_Dict],
|
15
|
+
data: typing.Optional[dict] = None,
|
16
|
+
client: typing.Optional[modal.client._Client] = None,
|
17
|
+
environment_name: typing.Optional[str] = None,
|
18
|
+
_heartbeat_sleep: float = 300,
|
19
|
+
) -> typing.AsyncContextManager[_Dict]: ...
|
23
20
|
@staticmethod
|
24
|
-
def from_name(
|
25
|
-
|
26
|
-
|
21
|
+
def from_name(
|
22
|
+
name: str,
|
23
|
+
data: typing.Optional[dict] = None,
|
24
|
+
namespace=1,
|
25
|
+
environment_name: typing.Optional[str] = None,
|
26
|
+
create_if_missing: bool = False,
|
27
|
+
) -> _Dict: ...
|
27
28
|
@staticmethod
|
28
|
-
def
|
29
|
-
|
30
|
-
|
29
|
+
async def lookup(
|
30
|
+
name: str,
|
31
|
+
data: typing.Optional[dict] = None,
|
32
|
+
namespace=1,
|
33
|
+
client: typing.Optional[modal.client._Client] = None,
|
34
|
+
environment_name: typing.Optional[str] = None,
|
35
|
+
create_if_missing: bool = False,
|
36
|
+
) -> _Dict: ...
|
31
37
|
@staticmethod
|
32
|
-
async def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
async def
|
39
|
-
|
40
|
-
|
41
|
-
async def
|
42
|
-
|
43
|
-
|
44
|
-
async def
|
45
|
-
|
46
|
-
|
47
|
-
async def
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
async def put(self, key: typing.Any, value: typing.Any) -> None:
|
54
|
-
...
|
55
|
-
|
56
|
-
async def __setitem__(self, key: typing.Any, value: typing.Any) -> None:
|
57
|
-
...
|
58
|
-
|
59
|
-
async def pop(self, key: typing.Any) -> typing.Any:
|
60
|
-
...
|
61
|
-
|
62
|
-
async def __delitem__(self, key: typing.Any) -> typing.Any:
|
63
|
-
...
|
64
|
-
|
65
|
-
async def __contains__(self, key: typing.Any) -> bool:
|
66
|
-
...
|
67
|
-
|
38
|
+
async def delete(
|
39
|
+
name: str,
|
40
|
+
*,
|
41
|
+
client: typing.Optional[modal.client._Client] = None,
|
42
|
+
environment_name: typing.Optional[str] = None,
|
43
|
+
): ...
|
44
|
+
async def clear(self) -> None: ...
|
45
|
+
async def get(self, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any: ...
|
46
|
+
async def contains(self, key: typing.Any) -> bool: ...
|
47
|
+
async def len(self) -> int: ...
|
48
|
+
async def __getitem__(self, key: typing.Any) -> typing.Any: ...
|
49
|
+
async def update(self, **kwargs) -> None: ...
|
50
|
+
async def put(self, key: typing.Any, value: typing.Any) -> None: ...
|
51
|
+
async def __setitem__(self, key: typing.Any, value: typing.Any) -> None: ...
|
52
|
+
async def pop(self, key: typing.Any) -> typing.Any: ...
|
53
|
+
async def __delitem__(self, key: typing.Any) -> typing.Any: ...
|
54
|
+
async def __contains__(self, key: typing.Any) -> bool: ...
|
55
|
+
def keys(self) -> collections.abc.AsyncIterator[typing.Any]: ...
|
56
|
+
def values(self) -> collections.abc.AsyncIterator[typing.Any]: ...
|
57
|
+
def items(self) -> collections.abc.AsyncIterator[tuple[typing.Any, typing.Any]]: ...
|
68
58
|
|
69
59
|
class Dict(modal.object.Object):
|
70
|
-
def __init__(self, data={}):
|
71
|
-
...
|
72
|
-
|
73
|
-
@staticmethod
|
74
|
-
def new(data: typing.Union[dict, None] = None) -> Dict:
|
75
|
-
...
|
76
|
-
|
60
|
+
def __init__(self, data={}): ...
|
77
61
|
@classmethod
|
78
|
-
def ephemeral(
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
62
|
+
def ephemeral(
|
63
|
+
cls: type[Dict],
|
64
|
+
data: typing.Optional[dict] = None,
|
65
|
+
client: typing.Optional[modal.client.Client] = None,
|
66
|
+
environment_name: typing.Optional[str] = None,
|
67
|
+
_heartbeat_sleep: float = 300,
|
68
|
+
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Dict]: ...
|
85
69
|
@staticmethod
|
86
|
-
def
|
87
|
-
|
70
|
+
def from_name(
|
71
|
+
name: str,
|
72
|
+
data: typing.Optional[dict] = None,
|
73
|
+
namespace=1,
|
74
|
+
environment_name: typing.Optional[str] = None,
|
75
|
+
create_if_missing: bool = False,
|
76
|
+
) -> Dict: ...
|
88
77
|
|
89
78
|
class __lookup_spec(typing_extensions.Protocol):
|
90
|
-
def __call__(
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
79
|
+
def __call__(
|
80
|
+
self,
|
81
|
+
name: str,
|
82
|
+
data: typing.Optional[dict] = None,
|
83
|
+
namespace=1,
|
84
|
+
client: typing.Optional[modal.client.Client] = None,
|
85
|
+
environment_name: typing.Optional[str] = None,
|
86
|
+
create_if_missing: bool = False,
|
87
|
+
) -> Dict: ...
|
88
|
+
async def aio(
|
89
|
+
self,
|
90
|
+
name: str,
|
91
|
+
data: typing.Optional[dict] = None,
|
92
|
+
namespace=1,
|
93
|
+
client: typing.Optional[modal.client.Client] = None,
|
94
|
+
environment_name: typing.Optional[str] = None,
|
95
|
+
create_if_missing: bool = False,
|
96
|
+
) -> Dict: ...
|
95
97
|
|
96
98
|
lookup: __lookup_spec
|
97
99
|
|
98
|
-
class
|
99
|
-
def __call__(
|
100
|
-
|
100
|
+
class __delete_spec(typing_extensions.Protocol):
|
101
|
+
def __call__(
|
102
|
+
self,
|
103
|
+
name: str,
|
104
|
+
*,
|
105
|
+
client: typing.Optional[modal.client.Client] = None,
|
106
|
+
environment_name: typing.Optional[str] = None,
|
107
|
+
): ...
|
108
|
+
async def aio(
|
109
|
+
self,
|
110
|
+
name: str,
|
111
|
+
*,
|
112
|
+
client: typing.Optional[modal.client.Client] = None,
|
113
|
+
environment_name: typing.Optional[str] = None,
|
114
|
+
): ...
|
115
|
+
|
116
|
+
delete: __delete_spec
|
101
117
|
|
102
|
-
|
103
|
-
|
118
|
+
class __clear_spec(typing_extensions.Protocol):
|
119
|
+
def __call__(self) -> None: ...
|
120
|
+
async def aio(self) -> None: ...
|
104
121
|
|
105
122
|
clear: __clear_spec
|
106
123
|
|
107
124
|
class __get_spec(typing_extensions.Protocol):
|
108
|
-
def __call__(self, key: typing.Any, default: typing.
|
109
|
-
|
110
|
-
|
111
|
-
async def aio(self, *args, **kwargs) -> typing.Any:
|
112
|
-
...
|
125
|
+
def __call__(self, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any: ...
|
126
|
+
async def aio(self, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any: ...
|
113
127
|
|
114
128
|
get: __get_spec
|
115
129
|
|
116
130
|
class __contains_spec(typing_extensions.Protocol):
|
117
|
-
def __call__(self, key: typing.Any) -> bool:
|
118
|
-
|
119
|
-
|
120
|
-
async def aio(self, *args, **kwargs) -> bool:
|
121
|
-
...
|
131
|
+
def __call__(self, key: typing.Any) -> bool: ...
|
132
|
+
async def aio(self, key: typing.Any) -> bool: ...
|
122
133
|
|
123
134
|
contains: __contains_spec
|
124
135
|
|
125
136
|
class __len_spec(typing_extensions.Protocol):
|
126
|
-
def __call__(self) -> int:
|
127
|
-
|
128
|
-
|
129
|
-
async def aio(self, *args, **kwargs) -> int:
|
130
|
-
...
|
137
|
+
def __call__(self) -> int: ...
|
138
|
+
async def aio(self) -> int: ...
|
131
139
|
|
132
140
|
len: __len_spec
|
133
141
|
|
134
142
|
class ____getitem___spec(typing_extensions.Protocol):
|
135
|
-
def __call__(self, key: typing.Any) -> typing.Any:
|
136
|
-
|
137
|
-
|
138
|
-
async def aio(self, *args, **kwargs) -> typing.Any:
|
139
|
-
...
|
143
|
+
def __call__(self, key: typing.Any) -> typing.Any: ...
|
144
|
+
async def aio(self, key: typing.Any) -> typing.Any: ...
|
140
145
|
|
141
146
|
__getitem__: ____getitem___spec
|
142
147
|
|
143
148
|
class __update_spec(typing_extensions.Protocol):
|
144
|
-
def __call__(self, **kwargs) -> None:
|
145
|
-
|
146
|
-
|
147
|
-
async def aio(self, *args, **kwargs) -> None:
|
148
|
-
...
|
149
|
+
def __call__(self, **kwargs) -> None: ...
|
150
|
+
async def aio(self, **kwargs) -> None: ...
|
149
151
|
|
150
152
|
update: __update_spec
|
151
153
|
|
152
154
|
class __put_spec(typing_extensions.Protocol):
|
153
|
-
def __call__(self, key: typing.Any, value: typing.Any) -> None:
|
154
|
-
|
155
|
-
|
156
|
-
async def aio(self, *args, **kwargs) -> None:
|
157
|
-
...
|
155
|
+
def __call__(self, key: typing.Any, value: typing.Any) -> None: ...
|
156
|
+
async def aio(self, key: typing.Any, value: typing.Any) -> None: ...
|
158
157
|
|
159
158
|
put: __put_spec
|
160
159
|
|
161
160
|
class ____setitem___spec(typing_extensions.Protocol):
|
162
|
-
def __call__(self, key: typing.Any, value: typing.Any) -> None:
|
163
|
-
|
164
|
-
|
165
|
-
async def aio(self, *args, **kwargs) -> None:
|
166
|
-
...
|
161
|
+
def __call__(self, key: typing.Any, value: typing.Any) -> None: ...
|
162
|
+
async def aio(self, key: typing.Any, value: typing.Any) -> None: ...
|
167
163
|
|
168
164
|
__setitem__: ____setitem___spec
|
169
165
|
|
170
166
|
class __pop_spec(typing_extensions.Protocol):
|
171
|
-
def __call__(self, key: typing.Any) -> typing.Any:
|
172
|
-
|
173
|
-
|
174
|
-
async def aio(self, *args, **kwargs) -> typing.Any:
|
175
|
-
...
|
167
|
+
def __call__(self, key: typing.Any) -> typing.Any: ...
|
168
|
+
async def aio(self, key: typing.Any) -> typing.Any: ...
|
176
169
|
|
177
170
|
pop: __pop_spec
|
178
171
|
|
179
172
|
class ____delitem___spec(typing_extensions.Protocol):
|
180
|
-
def __call__(self, key: typing.Any) -> typing.Any:
|
181
|
-
|
182
|
-
|
183
|
-
async def aio(self, *args, **kwargs) -> typing.Any:
|
184
|
-
...
|
173
|
+
def __call__(self, key: typing.Any) -> typing.Any: ...
|
174
|
+
async def aio(self, key: typing.Any) -> typing.Any: ...
|
185
175
|
|
186
176
|
__delitem__: ____delitem___spec
|
187
177
|
|
188
178
|
class ____contains___spec(typing_extensions.Protocol):
|
189
|
-
def __call__(self, key: typing.Any) -> bool:
|
190
|
-
|
191
|
-
|
192
|
-
async def aio(self, *args, **kwargs) -> bool:
|
193
|
-
...
|
179
|
+
def __call__(self, key: typing.Any) -> bool: ...
|
180
|
+
async def aio(self, key: typing.Any) -> bool: ...
|
194
181
|
|
195
182
|
__contains__: ____contains___spec
|
183
|
+
|
184
|
+
class __keys_spec(typing_extensions.Protocol):
|
185
|
+
def __call__(self) -> typing.Iterator[typing.Any]: ...
|
186
|
+
def aio(self) -> collections.abc.AsyncIterator[typing.Any]: ...
|
187
|
+
|
188
|
+
keys: __keys_spec
|
189
|
+
|
190
|
+
class __values_spec(typing_extensions.Protocol):
|
191
|
+
def __call__(self) -> typing.Iterator[typing.Any]: ...
|
192
|
+
def aio(self) -> collections.abc.AsyncIterator[typing.Any]: ...
|
193
|
+
|
194
|
+
values: __values_spec
|
195
|
+
|
196
|
+
class __items_spec(typing_extensions.Protocol):
|
197
|
+
def __call__(self) -> typing.Iterator[tuple[typing.Any, typing.Any]]: ...
|
198
|
+
def aio(self) -> collections.abc.AsyncIterator[tuple[typing.Any, typing.Any]]: ...
|
199
|
+
|
200
|
+
items: __items_spec
|
modal/environments.py
CHANGED
@@ -1,14 +1,115 @@
|
|
1
1
|
# Copyright Modal Labs 2023
|
2
|
-
from
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Optional
|
3
4
|
|
4
5
|
from google.protobuf.empty_pb2 import Empty
|
6
|
+
from google.protobuf.message import Message
|
5
7
|
from google.protobuf.wrappers_pb2 import StringValue
|
6
8
|
|
7
|
-
from modal.client import _Client
|
8
|
-
from modal.config import config
|
9
9
|
from modal_proto import api_pb2
|
10
10
|
|
11
|
-
from .
|
11
|
+
from ._resolver import Resolver
|
12
|
+
from ._utils.async_utils import synchronize_api, synchronizer
|
13
|
+
from ._utils.deprecation import renamed_parameter
|
14
|
+
from ._utils.grpc_utils import retry_transient_errors
|
15
|
+
from ._utils.name_utils import check_object_name
|
16
|
+
from .client import _Client
|
17
|
+
from .config import config, logger
|
18
|
+
from .object import _Object
|
19
|
+
|
20
|
+
|
21
|
+
@dataclass(frozen=True)
|
22
|
+
class EnvironmentSettings:
|
23
|
+
image_builder_version: str # Ideally would be typed with ImageBuilderVersion literal
|
24
|
+
webhook_suffix: str
|
25
|
+
|
26
|
+
|
27
|
+
class _Environment(_Object, type_prefix="en"):
|
28
|
+
_settings: EnvironmentSettings
|
29
|
+
|
30
|
+
def __init__(self):
|
31
|
+
"""mdmd:hidden"""
|
32
|
+
raise RuntimeError(
|
33
|
+
"`Environment(...)` constructor is not allowed."
|
34
|
+
" Please use `Environment.from_name` or `Environment.lookup` instead."
|
35
|
+
)
|
36
|
+
|
37
|
+
# TODO(michael) Keeping this private for now until we decide what else should be in it
|
38
|
+
# And what the rules should be about updates / mutability
|
39
|
+
# @property
|
40
|
+
# def settings(self) -> EnvironmentSettings:
|
41
|
+
# return self._settings
|
42
|
+
|
43
|
+
def _hydrate_metadata(self, metadata: Message):
|
44
|
+
# Overridden concrete implementation of base class method
|
45
|
+
assert metadata and isinstance(metadata, api_pb2.EnvironmentMetadata)
|
46
|
+
# TODO(michael) should probably expose the `name` from the metadata
|
47
|
+
# as the way to discover the name of the "default" environment
|
48
|
+
|
49
|
+
# Is there a simpler way to go Message -> Dataclass?
|
50
|
+
self._settings = EnvironmentSettings(
|
51
|
+
image_builder_version=metadata.settings.image_builder_version,
|
52
|
+
webhook_suffix=metadata.settings.webhook_suffix,
|
53
|
+
)
|
54
|
+
|
55
|
+
@staticmethod
|
56
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
57
|
+
async def from_name(
|
58
|
+
name: str,
|
59
|
+
create_if_missing: bool = False,
|
60
|
+
):
|
61
|
+
if name:
|
62
|
+
# Allow null names for the case where we want to look up the "default" environment,
|
63
|
+
# which is defined by the server. It feels messy to have "from_name" without a name, though?
|
64
|
+
# We're adding this mostly for internal use right now. We could consider an environment-only
|
65
|
+
# alternate constructor, like `Environment.get_default`, rather than exposing "unnamed"
|
66
|
+
# environments as part of public API when we make this class more useful.
|
67
|
+
check_object_name(name, "Environment")
|
68
|
+
|
69
|
+
async def _load(self: _Environment, resolver: Resolver, existing_object_id: Optional[str]):
|
70
|
+
request = api_pb2.EnvironmentGetOrCreateRequest(
|
71
|
+
deployment_name=name,
|
72
|
+
object_creation_type=(
|
73
|
+
api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING
|
74
|
+
if create_if_missing
|
75
|
+
else api_pb2.OBJECT_CREATION_TYPE_UNSPECIFIED
|
76
|
+
),
|
77
|
+
)
|
78
|
+
response = await retry_transient_errors(resolver.client.stub.EnvironmentGetOrCreate, request)
|
79
|
+
logger.debug(f"Created environment with id {response.environment_id}")
|
80
|
+
self._hydrate(response.environment_id, resolver.client, response.metadata)
|
81
|
+
|
82
|
+
# TODO environment name (and id?) in the repr? (We should make reprs consistently more useful)
|
83
|
+
return _Environment._from_loader(_load, "Environment()", is_another_app=True, hydrate_lazily=True)
|
84
|
+
|
85
|
+
@staticmethod
|
86
|
+
@renamed_parameter((2024, 12, 18), "label", "name")
|
87
|
+
async def lookup(
|
88
|
+
name: str,
|
89
|
+
client: Optional[_Client] = None,
|
90
|
+
create_if_missing: bool = False,
|
91
|
+
):
|
92
|
+
obj = await _Environment.from_name(name, create_if_missing=create_if_missing)
|
93
|
+
if client is None:
|
94
|
+
client = await _Client.from_env()
|
95
|
+
resolver = Resolver(client=client)
|
96
|
+
await resolver.load(obj)
|
97
|
+
return obj
|
98
|
+
|
99
|
+
|
100
|
+
Environment = synchronize_api(_Environment)
|
101
|
+
|
102
|
+
|
103
|
+
# Needs to be after definition; synchronicity interferes with forward references?
|
104
|
+
ENVIRONMENT_CACHE: dict[str, _Environment] = {}
|
105
|
+
|
106
|
+
|
107
|
+
async def _get_environment_cached(name: str, client: _Client) -> _Environment:
|
108
|
+
if name in ENVIRONMENT_CACHE:
|
109
|
+
return ENVIRONMENT_CACHE[name]
|
110
|
+
environment = await _Environment.lookup(name, client)
|
111
|
+
ENVIRONMENT_CACHE[name] = environment
|
112
|
+
return environment
|
12
113
|
|
13
114
|
|
14
115
|
@synchronizer.create_blocking
|
@@ -53,7 +154,7 @@ async def create_environment(name: str, client: Optional[_Client] = None):
|
|
53
154
|
|
54
155
|
|
55
156
|
@synchronizer.create_blocking
|
56
|
-
async def list_environments(client: Optional[_Client] = None) ->
|
157
|
+
async def list_environments(client: Optional[_Client] = None) -> list[api_pb2.EnvironmentListItem]:
|
57
158
|
if client is None:
|
58
159
|
client = await _Client.from_env()
|
59
160
|
resp = await client.stub.EnvironmentList(Empty())
|
modal/environments.pyi
CHANGED
@@ -1,47 +1,99 @@
|
|
1
|
+
import google.protobuf.message
|
1
2
|
import modal.client
|
3
|
+
import modal.object
|
2
4
|
import modal_proto.api_pb2
|
3
5
|
import typing
|
4
6
|
import typing_extensions
|
5
7
|
|
6
|
-
class
|
7
|
-
|
8
|
-
|
8
|
+
class EnvironmentSettings:
|
9
|
+
image_builder_version: str
|
10
|
+
webhook_suffix: str
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
+
def __init__(self, image_builder_version: str, webhook_suffix: str) -> None: ...
|
13
|
+
def __repr__(self): ...
|
14
|
+
def __eq__(self, other): ...
|
15
|
+
def __setattr__(self, name, value): ...
|
16
|
+
def __delattr__(self, name): ...
|
17
|
+
def __hash__(self): ...
|
12
18
|
|
13
|
-
|
19
|
+
class _Environment(modal.object._Object):
|
20
|
+
_settings: EnvironmentSettings
|
14
21
|
|
22
|
+
def __init__(self): ...
|
23
|
+
def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
|
24
|
+
@staticmethod
|
25
|
+
async def from_name(name: str, create_if_missing: bool = False): ...
|
26
|
+
@staticmethod
|
27
|
+
async def lookup(
|
28
|
+
name: str, client: typing.Optional[modal.client._Client] = None, create_if_missing: bool = False
|
29
|
+
): ...
|
15
30
|
|
16
|
-
class
|
17
|
-
|
18
|
-
...
|
31
|
+
class Environment(modal.object.Object):
|
32
|
+
_settings: EnvironmentSettings
|
19
33
|
|
20
|
-
|
21
|
-
|
34
|
+
def __init__(self): ...
|
35
|
+
def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
|
22
36
|
|
23
|
-
|
37
|
+
class __from_name_spec(typing_extensions.Protocol):
|
38
|
+
def __call__(self, name: str, create_if_missing: bool = False): ...
|
39
|
+
async def aio(self, name: str, create_if_missing: bool = False): ...
|
24
40
|
|
41
|
+
from_name: __from_name_spec
|
25
42
|
|
26
|
-
class
|
27
|
-
|
28
|
-
|
43
|
+
class __lookup_spec(typing_extensions.Protocol):
|
44
|
+
def __call__(
|
45
|
+
self, name: str, client: typing.Optional[modal.client.Client] = None, create_if_missing: bool = False
|
46
|
+
): ...
|
47
|
+
async def aio(
|
48
|
+
self, name: str, client: typing.Optional[modal.client.Client] = None, create_if_missing: bool = False
|
49
|
+
): ...
|
29
50
|
|
30
|
-
|
31
|
-
...
|
51
|
+
lookup: __lookup_spec
|
32
52
|
|
33
|
-
|
53
|
+
async def _get_environment_cached(name: str, client: modal.client._Client) -> _Environment: ...
|
34
54
|
|
55
|
+
class __delete_environment_spec(typing_extensions.Protocol):
|
56
|
+
def __call__(self, name: str, client: typing.Optional[modal.client.Client] = None): ...
|
57
|
+
async def aio(self, name: str, client: typing.Optional[modal.client.Client] = None): ...
|
35
58
|
|
36
|
-
|
37
|
-
def __call__(self, client: typing.Union[modal.client.Client, None] = None) -> typing.List[modal_proto.api_pb2.EnvironmentListItem]:
|
38
|
-
...
|
59
|
+
delete_environment: __delete_environment_spec
|
39
60
|
|
40
|
-
|
41
|
-
|
61
|
+
class __update_environment_spec(typing_extensions.Protocol):
|
62
|
+
def __call__(
|
63
|
+
self,
|
64
|
+
current_name: str,
|
65
|
+
*,
|
66
|
+
new_name: typing.Optional[str] = None,
|
67
|
+
new_web_suffix: typing.Optional[str] = None,
|
68
|
+
client: typing.Optional[modal.client.Client] = None,
|
69
|
+
): ...
|
70
|
+
async def aio(
|
71
|
+
self,
|
72
|
+
current_name: str,
|
73
|
+
*,
|
74
|
+
new_name: typing.Optional[str] = None,
|
75
|
+
new_web_suffix: typing.Optional[str] = None,
|
76
|
+
client: typing.Optional[modal.client.Client] = None,
|
77
|
+
): ...
|
78
|
+
|
79
|
+
update_environment: __update_environment_spec
|
80
|
+
|
81
|
+
class __create_environment_spec(typing_extensions.Protocol):
|
82
|
+
def __call__(self, name: str, client: typing.Optional[modal.client.Client] = None): ...
|
83
|
+
async def aio(self, name: str, client: typing.Optional[modal.client.Client] = None): ...
|
84
|
+
|
85
|
+
create_environment: __create_environment_spec
|
86
|
+
|
87
|
+
class __list_environments_spec(typing_extensions.Protocol):
|
88
|
+
def __call__(
|
89
|
+
self, client: typing.Optional[modal.client.Client] = None
|
90
|
+
) -> list[modal_proto.api_pb2.EnvironmentListItem]: ...
|
91
|
+
async def aio(
|
92
|
+
self, client: typing.Optional[modal.client.Client] = None
|
93
|
+
) -> list[modal_proto.api_pb2.EnvironmentListItem]: ...
|
42
94
|
|
43
95
|
list_environments: __list_environments_spec
|
44
96
|
|
97
|
+
def ensure_env(environment_name: typing.Optional[str] = None) -> str: ...
|
45
98
|
|
46
|
-
|
47
|
-
...
|
99
|
+
ENVIRONMENT_CACHE: dict[str, _Environment]
|