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.
Files changed (220) hide show
  1. modal/__init__.py +17 -13
  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 +420 -937
  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 -59
  11. modal/_resources.py +51 -0
  12. modal/_runtime/__init__.py +1 -0
  13. modal/_runtime/asgi.py +519 -0
  14. modal/_runtime/container_io_manager.py +1036 -0
  15. modal/_runtime/execution_context.py +89 -0
  16. modal/_runtime/telemetry.py +169 -0
  17. modal/_runtime/user_code_imports.py +356 -0
  18. modal/_serialization.py +134 -9
  19. modal/_traceback.py +47 -187
  20. modal/_tunnel.py +52 -16
  21. modal/_tunnel.pyi +19 -36
  22. modal/_utils/app_utils.py +3 -17
  23. modal/_utils/async_utils.py +479 -100
  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 +460 -171
  29. modal/_utils/grpc_testing.py +47 -31
  30. modal/_utils/grpc_utils.py +62 -109
  31. modal/_utils/hash_utils.py +61 -19
  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 +5 -7
  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 +14 -12
  43. modal/app.py +1003 -314
  44. modal/app.pyi +540 -264
  45. modal/call_graph.py +7 -6
  46. modal/cli/_download.py +63 -53
  47. modal/cli/_traceback.py +200 -0
  48. modal/cli/app.py +205 -45
  49. modal/cli/config.py +12 -5
  50. modal/cli/container.py +62 -14
  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 +64 -58
  55. modal/cli/launch.py +32 -18
  56. modal/cli/network_file_system.py +64 -83
  57. modal/cli/profile.py +1 -1
  58. modal/cli/programs/run_jupyter.py +35 -10
  59. modal/cli/programs/vscode.py +60 -10
  60. modal/cli/queues.py +131 -0
  61. modal/cli/run.py +234 -131
  62. modal/cli/secret.py +8 -7
  63. modal/cli/token.py +7 -2
  64. modal/cli/utils.py +79 -10
  65. modal/cli/volume.py +110 -109
  66. modal/client.py +250 -144
  67. modal/client.pyi +157 -118
  68. modal/cloud_bucket_mount.py +108 -34
  69. modal/cloud_bucket_mount.pyi +32 -38
  70. modal/cls.py +535 -148
  71. modal/cls.pyi +190 -146
  72. modal/config.py +41 -19
  73. modal/container_process.py +177 -0
  74. modal/container_process.pyi +82 -0
  75. modal/dict.py +111 -65
  76. modal/dict.pyi +136 -131
  77. modal/environments.py +106 -5
  78. modal/environments.pyi +77 -25
  79. modal/exception.py +34 -43
  80. modal/experimental.py +61 -2
  81. modal/extensions/ipython.py +5 -5
  82. modal/file_io.py +537 -0
  83. modal/file_io.pyi +235 -0
  84. modal/file_pattern_matcher.py +197 -0
  85. modal/functions.py +906 -911
  86. modal/functions.pyi +466 -430
  87. modal/gpu.py +57 -44
  88. modal/image.py +1089 -479
  89. modal/image.pyi +584 -228
  90. modal/io_streams.py +434 -0
  91. modal/io_streams.pyi +122 -0
  92. modal/mount.py +314 -101
  93. modal/mount.pyi +241 -235
  94. modal/network_file_system.py +92 -92
  95. modal/network_file_system.pyi +152 -110
  96. modal/object.py +67 -36
  97. modal/object.pyi +166 -143
  98. modal/output.py +63 -0
  99. modal/parallel_map.py +434 -0
  100. modal/parallel_map.pyi +75 -0
  101. modal/partial_function.py +282 -117
  102. modal/partial_function.pyi +222 -129
  103. modal/proxy.py +15 -12
  104. modal/proxy.pyi +3 -8
  105. modal/queue.py +182 -65
  106. modal/queue.pyi +218 -118
  107. modal/requirements/2024.04.txt +29 -0
  108. modal/requirements/2024.10.txt +16 -0
  109. modal/requirements/README.md +21 -0
  110. modal/requirements/base-images.json +22 -0
  111. modal/retries.py +48 -7
  112. modal/runner.py +459 -156
  113. modal/runner.pyi +135 -71
  114. modal/running_app.py +38 -0
  115. modal/sandbox.py +514 -236
  116. modal/sandbox.pyi +397 -169
  117. modal/schedule.py +4 -4
  118. modal/scheduler_placement.py +20 -3
  119. modal/secret.py +56 -31
  120. modal/secret.pyi +62 -42
  121. modal/serving.py +51 -56
  122. modal/serving.pyi +44 -36
  123. modal/stream_type.py +15 -0
  124. modal/token_flow.py +5 -3
  125. modal/token_flow.pyi +37 -32
  126. modal/volume.py +285 -157
  127. modal/volume.pyi +249 -184
  128. {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/METADATA +7 -7
  129. modal-0.72.11.dist-info/RECORD +174 -0
  130. {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/top_level.txt +0 -1
  131. modal_docs/gen_reference_docs.py +3 -1
  132. modal_docs/mdmd/mdmd.py +0 -1
  133. modal_docs/mdmd/signatures.py +5 -2
  134. modal_global_objects/images/base_images.py +28 -0
  135. modal_global_objects/mounts/python_standalone.py +2 -2
  136. modal_proto/__init__.py +1 -1
  137. modal_proto/api.proto +1288 -533
  138. modal_proto/api_grpc.py +856 -456
  139. modal_proto/api_pb2.py +2165 -1157
  140. modal_proto/api_pb2.pyi +8859 -0
  141. modal_proto/api_pb2_grpc.py +1674 -855
  142. modal_proto/api_pb2_grpc.pyi +1416 -0
  143. modal_proto/modal_api_grpc.py +149 -0
  144. modal_proto/modal_options_grpc.py +3 -0
  145. modal_proto/options_pb2.pyi +20 -0
  146. modal_proto/options_pb2_grpc.pyi +7 -0
  147. modal_proto/py.typed +0 -0
  148. modal_version/__init__.py +1 -1
  149. modal_version/_version_generated.py +2 -2
  150. modal/_asgi.py +0 -370
  151. modal/_container_entrypoint.pyi +0 -378
  152. modal/_container_exec.py +0 -128
  153. modal/_sandbox_shell.py +0 -49
  154. modal/shared_volume.py +0 -23
  155. modal/shared_volume.pyi +0 -24
  156. modal/stub.py +0 -783
  157. modal/stub.pyi +0 -332
  158. modal-0.62.16.dist-info/RECORD +0 -198
  159. modal_global_objects/images/conda.py +0 -15
  160. modal_global_objects/images/debian_slim.py +0 -15
  161. modal_global_objects/images/micromamba.py +0 -15
  162. test/__init__.py +0 -1
  163. test/aio_test.py +0 -12
  164. test/async_utils_test.py +0 -262
  165. test/blob_test.py +0 -67
  166. test/cli_imports_test.py +0 -149
  167. test/cli_test.py +0 -659
  168. test/client_test.py +0 -194
  169. test/cls_test.py +0 -630
  170. test/config_test.py +0 -137
  171. test/conftest.py +0 -1420
  172. test/container_app_test.py +0 -32
  173. test/container_test.py +0 -1389
  174. test/cpu_test.py +0 -23
  175. test/decorator_test.py +0 -85
  176. test/deprecation_test.py +0 -34
  177. test/dict_test.py +0 -33
  178. test/e2e_test.py +0 -68
  179. test/error_test.py +0 -7
  180. test/function_serialization_test.py +0 -32
  181. test/function_test.py +0 -653
  182. test/function_utils_test.py +0 -101
  183. test/gpu_test.py +0 -159
  184. test/grpc_utils_test.py +0 -141
  185. test/helpers.py +0 -42
  186. test/image_test.py +0 -669
  187. test/live_reload_test.py +0 -80
  188. test/lookup_test.py +0 -70
  189. test/mdmd_test.py +0 -329
  190. test/mount_test.py +0 -162
  191. test/mounted_files_test.py +0 -329
  192. test/network_file_system_test.py +0 -181
  193. test/notebook_test.py +0 -66
  194. test/object_test.py +0 -41
  195. test/package_utils_test.py +0 -25
  196. test/queue_test.py +0 -97
  197. test/resolver_test.py +0 -58
  198. test/retries_test.py +0 -67
  199. test/runner_test.py +0 -85
  200. test/sandbox_test.py +0 -191
  201. test/schedule_test.py +0 -15
  202. test/scheduler_placement_test.py +0 -29
  203. test/secret_test.py +0 -78
  204. test/serialization_test.py +0 -42
  205. test/stub_composition_test.py +0 -10
  206. test/stub_test.py +0 -360
  207. test/test_asgi_wrapper.py +0 -234
  208. test/token_flow_test.py +0 -18
  209. test/traceback_test.py +0 -135
  210. test/tunnel_test.py +0 -29
  211. test/utils_test.py +0 -88
  212. test/version_test.py +0 -14
  213. test/volume_test.py +0 -341
  214. test/watcher_test.py +0 -30
  215. test/webhook_test.py +0 -146
  216. /modal/{requirements.312.txt → requirements/2023.12.312.txt} +0 -0
  217. /modal/{requirements.txt → requirements/2023.12.txt} +0 -0
  218. {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/LICENSE +0 -0
  219. {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/WHEEL +0 -0
  220. {modal-0.62.16.dist-info → modal-0.72.11.dist-info}/entry_points.txt +0 -0
modal/cls.pyi CHANGED
@@ -1,5 +1,7 @@
1
+ import collections.abc
1
2
  import google.protobuf.message
2
- import modal._output
3
+ import inspect
4
+ import modal.app
3
5
  import modal.client
4
6
  import modal.functions
5
7
  import modal.gpu
@@ -7,7 +9,6 @@ import modal.object
7
9
  import modal.partial_function
8
10
  import modal.retries
9
11
  import modal.secret
10
- import modal.stub
11
12
  import modal.volume
12
13
  import modal_proto.api_pb2
13
14
  import os
@@ -16,167 +17,210 @@ import typing_extensions
16
17
 
17
18
  T = typing.TypeVar("T")
18
19
 
19
- class _Obj:
20
- _functions: typing.Dict[str, modal.functions._Function]
21
- _inited: bool
22
- _entered: bool
23
- _local_obj: typing.Any
24
- _local_obj_constr: typing.Union[typing.Callable[[], typing.Any], None]
25
-
26
- def __init__(self, user_cls: type, output_mgr: typing.Union[modal._output.OutputManager, None], base_functions: typing.Dict[str, modal.functions._Function], from_other_workspace: bool, options: typing.Union[modal_proto.api_pb2.FunctionOptions, None], args, kwargs):
27
- ...
28
-
29
- def get_obj(self):
30
- ...
31
-
32
- def get_local_obj(self):
33
- ...
34
-
35
- def enter(self):
36
- ...
20
+ def _use_annotation_parameters(user_cls: type) -> bool: ...
21
+ def _get_class_constructor_signature(user_cls: type) -> inspect.Signature: ...
22
+ def _bind_instance_method(
23
+ service_function: modal.functions._Function, class_bound_method: modal.functions._Function
24
+ ): ...
37
25
 
26
+ class _Obj:
27
+ _cls: _Cls
28
+ _functions: dict[str, modal.functions._Function]
29
+ _has_entered: bool
30
+ _user_cls_instance: typing.Optional[typing.Any]
31
+ _args: tuple[typing.Any, ...]
32
+ _kwargs: dict[str, typing.Any]
33
+ _instance_service_function: typing.Optional[modal.functions._Function]
34
+
35
+ def _uses_common_service_function(self): ...
36
+ def __init__(
37
+ self,
38
+ cls: _Cls,
39
+ user_cls: typing.Optional[type],
40
+ options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
41
+ args,
42
+ kwargs,
43
+ ): ...
44
+ def _cached_service_function(self) -> modal.functions._Function: ...
45
+ def _get_parameter_values(self) -> dict[str, typing.Any]: ...
46
+ def _new_user_cls_instance(self): ...
47
+ async def keep_warm(self, warm_pool_size: int) -> None: ...
48
+ def _cached_user_cls_instance(self): ...
49
+ def _enter(self): ...
38
50
  @property
39
- def entered(self):
40
- ...
41
-
42
- @entered.setter
43
- def entered(self, val):
44
- ...
45
-
46
- async def aenter(self):
47
- ...
48
-
49
- def __getattr__(self, k):
50
- ...
51
-
51
+ def _entered(self) -> bool: ...
52
+ @_entered.setter
53
+ def _entered(self, val: bool): ...
54
+ async def _aenter(self): ...
55
+ def __getattr__(self, k): ...
52
56
 
53
57
  class Obj:
54
- _functions: typing.Dict[str, modal.functions.Function]
55
- _inited: bool
56
- _entered: bool
57
- _local_obj: typing.Any
58
- _local_obj_constr: typing.Union[typing.Callable[[], typing.Any], None]
59
-
60
- def __init__(self, user_cls: type, output_mgr: typing.Union[modal._output.OutputManager, None], base_functions: typing.Dict[str, modal.functions.Function], from_other_workspace: bool, options: typing.Union[modal_proto.api_pb2.FunctionOptions, None], args, kwargs):
61
- ...
62
-
63
- def get_obj(self):
64
- ...
65
-
66
- def get_local_obj(self):
67
- ...
68
-
69
- def enter(self):
70
- ...
71
-
58
+ _cls: Cls
59
+ _functions: dict[str, modal.functions.Function]
60
+ _has_entered: bool
61
+ _user_cls_instance: typing.Optional[typing.Any]
62
+ _args: tuple[typing.Any, ...]
63
+ _kwargs: dict[str, typing.Any]
64
+ _instance_service_function: typing.Optional[modal.functions.Function]
65
+
66
+ def __init__(
67
+ self,
68
+ cls: Cls,
69
+ user_cls: typing.Optional[type],
70
+ options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
71
+ args,
72
+ kwargs,
73
+ ): ...
74
+ def _uses_common_service_function(self): ...
75
+ def _cached_service_function(self) -> modal.functions.Function: ...
76
+ def _get_parameter_values(self) -> dict[str, typing.Any]: ...
77
+ def _new_user_cls_instance(self): ...
78
+
79
+ class __keep_warm_spec(typing_extensions.Protocol):
80
+ def __call__(self, warm_pool_size: int) -> None: ...
81
+ async def aio(self, warm_pool_size: int) -> None: ...
82
+
83
+ keep_warm: __keep_warm_spec
84
+
85
+ def _cached_user_cls_instance(self): ...
86
+ def _enter(self): ...
72
87
  @property
73
- def entered(self):
74
- ...
75
-
76
- @entered.setter
77
- def entered(self, val):
78
- ...
79
-
80
- async def aenter(self):
81
- ...
82
-
83
- def __getattr__(self, k):
84
- ...
85
-
88
+ def _entered(self) -> bool: ...
89
+ @_entered.setter
90
+ def _entered(self, val: bool): ...
91
+ async def _aenter(self): ...
92
+ def __getattr__(self, k): ...
86
93
 
87
94
  class _Cls(modal.object._Object):
88
- _user_cls: typing.Union[type, None]
89
- _functions: typing.Dict[str, modal.functions._Function]
90
- _options: typing.Union[modal_proto.api_pb2.FunctionOptions, None]
91
- _callables: typing.Dict[str, typing.Callable]
92
- _from_other_workspace: typing.Union[bool, None]
93
- _stub: typing.Union[modal.stub._Stub, None]
94
-
95
- def _initialize_from_empty(self):
96
- ...
97
-
98
- def _initialize_from_other(self, other: _Cls):
99
- ...
100
-
101
- def _set_output_mgr(self, output_mgr: modal._output.OutputManager):
102
- ...
103
-
104
- def _hydrate_metadata(self, metadata: google.protobuf.message.Message):
105
- ...
106
-
107
- def _get_metadata(self) -> modal_proto.api_pb2.ClassHandleMetadata:
108
- ...
109
-
95
+ _user_cls: typing.Optional[type]
96
+ _class_service_function: typing.Optional[modal.functions._Function]
97
+ _method_functions: typing.Optional[dict[str, modal.functions._Function]]
98
+ _options: typing.Optional[modal_proto.api_pb2.FunctionOptions]
99
+ _callables: dict[str, typing.Callable[..., typing.Any]]
100
+ _app: typing.Optional[modal.app._App]
101
+ _name: typing.Optional[str]
102
+
103
+ def _initialize_from_empty(self): ...
104
+ def _initialize_from_other(self, other: _Cls): ...
105
+ def _get_partial_functions(self) -> dict[str, modal.partial_function._PartialFunction]: ...
106
+ def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
110
107
  @staticmethod
111
- def from_local(user_cls, stub, decorator: typing.Callable[[modal.partial_function.PartialFunction, type], modal.functions._Function]) -> _Cls:
112
- ...
113
-
108
+ def validate_construction_mechanism(user_cls): ...
109
+ @staticmethod
110
+ def from_local(user_cls, app: modal.app._App, class_service_function: modal.functions._Function) -> _Cls: ...
111
+ def _uses_common_service_function(self): ...
114
112
  @classmethod
115
- def from_name(cls: typing.Type[_Cls], app_name: str, tag: typing.Union[str, None] = None, namespace=1, environment_name: typing.Union[str, None] = None, workspace: typing.Union[str, None] = None) -> _Cls:
116
- ...
117
-
118
- def with_options(self: _Cls, cpu: typing.Union[float, None] = None, memory: typing.Union[int, None] = None, gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None, secrets: typing.Collection[modal.secret._Secret] = (), volumes: typing.Dict[typing.Union[str, os.PathLike], modal.volume._Volume] = {}, retries: typing.Union[int, modal.retries.Retries, None] = None, timeout: typing.Union[int, None] = None, concurrency_limit: typing.Union[int, None] = None, allow_concurrent_inputs: typing.Union[int, None] = None, container_idle_timeout: typing.Union[int, None] = None, allow_background_volume_commits: bool = False) -> _Cls:
119
- ...
120
-
113
+ def from_name(
114
+ cls: type[_Cls],
115
+ app_name: str,
116
+ name: str,
117
+ namespace=1,
118
+ environment_name: typing.Optional[str] = None,
119
+ workspace: typing.Optional[str] = None,
120
+ ) -> _Cls: ...
121
+ def with_options(
122
+ self: _Cls,
123
+ cpu: typing.Union[float, tuple[float, float], None] = None,
124
+ memory: typing.Union[int, tuple[int, int], None] = None,
125
+ gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
126
+ secrets: collections.abc.Collection[modal.secret._Secret] = (),
127
+ volumes: dict[typing.Union[str, os.PathLike], modal.volume._Volume] = {},
128
+ retries: typing.Union[int, modal.retries.Retries, None] = None,
129
+ timeout: typing.Optional[int] = None,
130
+ concurrency_limit: typing.Optional[int] = None,
131
+ allow_concurrent_inputs: typing.Optional[int] = None,
132
+ container_idle_timeout: typing.Optional[int] = None,
133
+ ) -> _Cls: ...
121
134
  @staticmethod
122
- async def lookup(app_name: str, tag: typing.Union[str, None] = None, namespace=1, client: typing.Union[modal.client._Client, None] = None, environment_name: typing.Union[str, None] = None, workspace: typing.Union[str, None] = None) -> _Cls:
123
- ...
124
-
125
- def __call__(self, *args, **kwargs) -> _Obj:
126
- ...
127
-
128
- def __getattr__(self, k):
129
- ...
130
-
135
+ async def lookup(
136
+ app_name: str,
137
+ name: str,
138
+ namespace=1,
139
+ client: typing.Optional[modal.client._Client] = None,
140
+ environment_name: typing.Optional[str] = None,
141
+ workspace: typing.Optional[str] = None,
142
+ ) -> _Cls: ...
143
+ def __call__(self, *args, **kwargs) -> _Obj: ...
144
+ def __getattr__(self, k): ...
131
145
 
132
146
  class Cls(modal.object.Object):
133
- _user_cls: typing.Union[type, None]
134
- _functions: typing.Dict[str, modal.functions.Function]
135
- _options: typing.Union[modal_proto.api_pb2.FunctionOptions, None]
136
- _callables: typing.Dict[str, typing.Callable]
137
- _from_other_workspace: typing.Union[bool, None]
138
- _stub: typing.Union[modal.stub.Stub, None]
139
-
140
- def __init__(self, *args, **kwargs):
141
- ...
142
-
143
- def _initialize_from_empty(self):
144
- ...
145
-
146
- def _initialize_from_other(self, other: Cls):
147
- ...
148
-
149
- def _set_output_mgr(self, output_mgr: modal._output.OutputManager):
150
- ...
151
-
152
- def _hydrate_metadata(self, metadata: google.protobuf.message.Message):
153
- ...
154
-
155
- def _get_metadata(self) -> modal_proto.api_pb2.ClassHandleMetadata:
156
- ...
157
-
147
+ _user_cls: typing.Optional[type]
148
+ _class_service_function: typing.Optional[modal.functions.Function]
149
+ _method_functions: typing.Optional[dict[str, modal.functions.Function]]
150
+ _options: typing.Optional[modal_proto.api_pb2.FunctionOptions]
151
+ _callables: dict[str, typing.Callable[..., typing.Any]]
152
+ _app: typing.Optional[modal.app.App]
153
+ _name: typing.Optional[str]
154
+
155
+ def __init__(self, *args, **kwargs): ...
156
+ def _initialize_from_empty(self): ...
157
+ def _initialize_from_other(self, other: Cls): ...
158
+ def _get_partial_functions(self) -> dict[str, modal.partial_function.PartialFunction]: ...
159
+ def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
158
160
  @staticmethod
159
- def from_local(user_cls, stub, decorator: typing.Callable[[modal.partial_function.PartialFunction, type], modal.functions.Function]) -> Cls:
160
- ...
161
-
161
+ def validate_construction_mechanism(user_cls): ...
162
+ @staticmethod
163
+ def from_local(user_cls, app: modal.app.App, class_service_function: modal.functions.Function) -> Cls: ...
164
+ def _uses_common_service_function(self): ...
162
165
  @classmethod
163
- def from_name(cls: typing.Type[Cls], app_name: str, tag: typing.Union[str, None] = None, namespace=1, environment_name: typing.Union[str, None] = None, workspace: typing.Union[str, None] = None) -> Cls:
164
- ...
165
-
166
- def with_options(self: Cls, cpu: typing.Union[float, None] = None, memory: typing.Union[int, None] = None, gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None, secrets: typing.Collection[modal.secret.Secret] = (), volumes: typing.Dict[typing.Union[str, os.PathLike], modal.volume.Volume] = {}, retries: typing.Union[int, modal.retries.Retries, None] = None, timeout: typing.Union[int, None] = None, concurrency_limit: typing.Union[int, None] = None, allow_concurrent_inputs: typing.Union[int, None] = None, container_idle_timeout: typing.Union[int, None] = None, allow_background_volume_commits: bool = False) -> Cls:
167
- ...
166
+ def from_name(
167
+ cls: type[Cls],
168
+ app_name: str,
169
+ name: str,
170
+ namespace=1,
171
+ environment_name: typing.Optional[str] = None,
172
+ workspace: typing.Optional[str] = None,
173
+ ) -> Cls: ...
174
+ def with_options(
175
+ self: Cls,
176
+ cpu: typing.Union[float, tuple[float, float], None] = None,
177
+ memory: typing.Union[int, tuple[int, int], None] = None,
178
+ gpu: typing.Union[None, bool, str, modal.gpu._GPUConfig] = None,
179
+ secrets: collections.abc.Collection[modal.secret.Secret] = (),
180
+ volumes: dict[typing.Union[str, os.PathLike], modal.volume.Volume] = {},
181
+ retries: typing.Union[int, modal.retries.Retries, None] = None,
182
+ timeout: typing.Optional[int] = None,
183
+ concurrency_limit: typing.Optional[int] = None,
184
+ allow_concurrent_inputs: typing.Optional[int] = None,
185
+ container_idle_timeout: typing.Optional[int] = None,
186
+ ) -> Cls: ...
168
187
 
169
188
  class __lookup_spec(typing_extensions.Protocol):
170
- def __call__(self, app_name: str, tag: typing.Union[str, None] = None, namespace=1, client: typing.Union[modal.client.Client, None] = None, environment_name: typing.Union[str, None] = None, workspace: typing.Union[str, None] = None) -> Cls:
171
- ...
172
-
173
- async def aio(self, *args, **kwargs) -> Cls:
174
- ...
189
+ def __call__(
190
+ self,
191
+ app_name: str,
192
+ name: str,
193
+ namespace=1,
194
+ client: typing.Optional[modal.client.Client] = None,
195
+ environment_name: typing.Optional[str] = None,
196
+ workspace: typing.Optional[str] = None,
197
+ ) -> Cls: ...
198
+ async def aio(
199
+ self,
200
+ app_name: str,
201
+ name: str,
202
+ namespace=1,
203
+ client: typing.Optional[modal.client.Client] = None,
204
+ environment_name: typing.Optional[str] = None,
205
+ workspace: typing.Optional[str] = None,
206
+ ) -> Cls: ...
175
207
 
176
208
  lookup: __lookup_spec
177
209
 
178
- def __call__(self, *args, **kwargs) -> Obj:
179
- ...
210
+ def __call__(self, *args, **kwargs) -> Obj: ...
211
+ def __getattr__(self, k): ...
212
+
213
+ class _NO_DEFAULT:
214
+ def __repr__(self): ...
215
+
216
+ _no_default: _NO_DEFAULT
217
+
218
+ class _Parameter:
219
+ default: typing.Any
220
+ init: bool
221
+
222
+ def __init__(self, default: typing.Any, init: bool): ...
223
+ def __get__(self, obj, obj_type=None) -> typing.Any: ...
180
224
 
181
- def __getattr__(self, k):
182
- ...
225
+ def is_parameter(p: typing.Any) -> bool: ...
226
+ def parameter(*, default: typing.Any = modal.cls._NO_DEFAULT(), init: bool = True) -> typing.Any: ...
modal/config.py CHANGED
@@ -29,7 +29,7 @@ Setting tokens using the CLI
29
29
 
30
30
  You can set a token by running the command::
31
31
 
32
- ```bash
32
+ ```
33
33
  modal token set \
34
34
  --token-id <token id> \
35
35
  --token-secret <token secret>
@@ -55,6 +55,11 @@ Other possible configuration options are:
55
55
  Defaults to True.
56
56
  By default, Modal automatically mounts modules imported in the current scope, that
57
57
  are deemed to be "local". This can be turned off by setting this to False.
58
+ * `force_build` (in the .toml file) / `MODAL_FORCE_BUILD` (as an env var).
59
+ Defaults to False.
60
+ When set, ignores the Image cache and builds all Image layers. Note that this
61
+ will break the cache for all images based on the rebuilt layers, so other images
62
+ may rebuild on subsequent runs / deploys even if the config is reverted.
58
63
  * `traceback` (in the .toml file) / `MODAL_TRACEBACK` (as an env var).
59
64
  Defaults to False. Enables printing full tracebacks on unexpected CLI
60
65
  errors, which can be useful for debugging client issues.
@@ -75,14 +80,15 @@ import os
75
80
  import typing
76
81
  import warnings
77
82
  from textwrap import dedent
78
- from typing import Any, Dict, Optional
83
+ from typing import Any, Optional
79
84
 
80
85
  from google.protobuf.empty_pb2 import Empty
81
86
 
82
87
  from modal_proto import api_pb2
83
88
 
89
+ from ._utils.deprecation import deprecation_error
84
90
  from ._utils.logger import configure_logger
85
- from .exception import InvalidError, deprecation_warning
91
+ from .exception import InvalidError
86
92
 
87
93
  # Locate config file and read it
88
94
 
@@ -98,14 +104,25 @@ def _is_remote() -> bool:
98
104
 
99
105
 
100
106
  def _read_user_config():
107
+ config_data = {}
101
108
  if not _is_remote() and os.path.exists(user_config_path):
102
109
  # Defer toml import so we don't need it in the container runtime environment
103
110
  import toml
104
111
 
105
- with open(user_config_path) as f:
106
- return toml.load(f)
107
- else:
108
- return {}
112
+ try:
113
+ with open(user_config_path) as f:
114
+ config_data = toml.load(f)
115
+ except Exception as exc:
116
+ config_problem = str(exc)
117
+ else:
118
+ if not all(isinstance(e, dict) for e in config_data.values()):
119
+ config_problem = "TOML file must contain table sections for each profile."
120
+ else:
121
+ config_problem = ""
122
+ if config_problem:
123
+ message = f"\nError when reading the modal configuration from `{user_config_path}`.\n\n{config_problem}"
124
+ raise InvalidError(message)
125
+ return config_data
109
126
 
110
127
 
111
128
  _user_config = _read_user_config()
@@ -161,11 +178,9 @@ def _check_config() -> None:
161
178
  Support for using an implicit 'default' profile is deprecated.
162
179
  Please use `modal profile activate` to activate one of your profiles.
163
180
  (Use `modal profile list` to see the options.)
164
-
165
- This will become an error in a future update.
166
181
  """
167
182
  )
168
- deprecation_warning((2024, 2, 6), message, show_source=False)
183
+ deprecation_error((2024, 2, 6), message)
169
184
 
170
185
 
171
186
  _profile = os.environ.get("MODAL_PROFILE") or _config_active_profile()
@@ -173,6 +188,10 @@ _profile = os.environ.get("MODAL_PROFILE") or _config_active_profile()
173
188
  # Define settings
174
189
 
175
190
 
191
+ def _to_boolean(x: object) -> bool:
192
+ return str(x).lower() not in {"", "0", "false"}
193
+
194
+
176
195
  class _Setting(typing.NamedTuple):
177
196
  default: typing.Any = None
178
197
  transform: typing.Callable[[str], typing.Any] = lambda x: x # noqa: E731
@@ -185,22 +204,25 @@ _SETTINGS = {
185
204
  "token_id": _Setting(),
186
205
  "token_secret": _Setting(),
187
206
  "task_id": _Setting(),
188
- "task_secret": _Setting(),
189
207
  "serve_timeout": _Setting(transform=float),
190
208
  "sync_entrypoint": _Setting(),
191
209
  "logs_timeout": _Setting(10, float),
192
210
  "image_id": _Setting(),
193
- "automount": _Setting(True, transform=lambda x: x not in ("", "0")),
194
- "profiling_enabled": _Setting(False, transform=lambda x: x not in ("", "0")),
211
+ "automount": _Setting(True, transform=_to_boolean),
195
212
  "heartbeat_interval": _Setting(15, float),
196
213
  "function_runtime": _Setting(),
197
- "function_runtime_debug": _Setting(False, transform=lambda x: x not in ("", "0")), # For internal debugging use.
214
+ "function_runtime_debug": _Setting(False, transform=_to_boolean), # For internal debugging use.
215
+ "runtime_perf_record": _Setting(False, transform=_to_boolean), # For internal debugging use.
198
216
  "environment": _Setting(),
199
217
  "default_cloud": _Setting(None, transform=lambda x: x if x else None),
200
218
  "worker_id": _Setting(), # For internal debugging use.
201
219
  "restore_state_path": _Setting("/__modal/restore-state.json"),
202
- "force_build": _Setting(False, transform=lambda x: x not in ("", "0")),
203
- "traceback": _Setting(False, transform=lambda x: x not in ("", "0")),
220
+ "force_build": _Setting(False, transform=_to_boolean),
221
+ "traceback": _Setting(False, transform=_to_boolean),
222
+ "image_builder_version": _Setting(),
223
+ "strict_parameters": _Setting(False, transform=_to_boolean), # For internal/experimental use
224
+ "snapshot_debug": _Setting(False, transform=_to_boolean),
225
+ "client_retries": _Setting(False, transform=_to_boolean), # For internal testing.
204
226
  }
205
227
 
206
228
 
@@ -238,7 +260,7 @@ class Config:
238
260
  os.environ["MODAL_" + key.upper()] = value
239
261
  except KeyError:
240
262
  # Override env vars not available in config, e.g. NVIDIA_VISIBLE_DEVICES.
241
- # This is used for restoring env vars from a checkpoint.
263
+ # This is used for restoring env vars from a memory snapshot.
242
264
  os.environ[key.upper()] = value
243
265
 
244
266
  def __getitem__(self, key):
@@ -248,7 +270,7 @@ class Config:
248
270
  return repr(self.to_dict())
249
271
 
250
272
  def to_dict(self):
251
- return {key: self.get(key) for key in _SETTINGS.keys()}
273
+ return {key: self.get(key) for key in sorted(_SETTINGS)}
252
274
 
253
275
 
254
276
  config = Config()
@@ -262,7 +284,7 @@ configure_logger(logger, config["loglevel"], config["log_format"])
262
284
 
263
285
 
264
286
  def _store_user_config(
265
- new_settings: Dict[str, Any], profile: Optional[str] = None, active_profile: Optional[str] = None
287
+ new_settings: dict[str, Any], profile: Optional[str] = None, active_profile: Optional[str] = None
266
288
  ):
267
289
  """Internal method, used by the CLI to set tokens."""
268
290
  if profile is None: