modal 0.74.44__tar.gz → 0.74.46__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.
Files changed (183) hide show
  1. {modal-0.74.44 → modal-0.74.46}/PKG-INFO +1 -1
  2. {modal-0.74.44 → modal-0.74.46}/modal/_functions.py +62 -10
  3. {modal-0.74.44 → modal-0.74.46}/modal/client.pyi +2 -2
  4. {modal-0.74.44 → modal-0.74.46}/modal/cls.py +56 -8
  5. {modal-0.74.44 → modal-0.74.46}/modal/cls.pyi +28 -0
  6. {modal-0.74.44 → modal-0.74.46}/modal/experimental/__init__.py +8 -0
  7. {modal-0.74.44 → modal-0.74.46}/modal/functions.pyi +20 -0
  8. {modal-0.74.44 → modal-0.74.46}/modal/secret.py +3 -3
  9. {modal-0.74.44 → modal-0.74.46}/modal/secret.pyi +6 -6
  10. {modal-0.74.44 → modal-0.74.46}/modal.egg-info/PKG-INFO +1 -1
  11. {modal-0.74.44 → modal-0.74.46}/modal_version/_version_generated.py +1 -1
  12. {modal-0.74.44 → modal-0.74.46}/LICENSE +0 -0
  13. {modal-0.74.44 → modal-0.74.46}/README.md +0 -0
  14. {modal-0.74.44 → modal-0.74.46}/modal/__init__.py +0 -0
  15. {modal-0.74.44 → modal-0.74.46}/modal/__main__.py +0 -0
  16. {modal-0.74.44 → modal-0.74.46}/modal/_clustered_functions.py +0 -0
  17. {modal-0.74.44 → modal-0.74.46}/modal/_clustered_functions.pyi +0 -0
  18. {modal-0.74.44 → modal-0.74.46}/modal/_container_entrypoint.py +0 -0
  19. {modal-0.74.44 → modal-0.74.46}/modal/_ipython.py +0 -0
  20. {modal-0.74.44 → modal-0.74.46}/modal/_location.py +0 -0
  21. {modal-0.74.44 → modal-0.74.46}/modal/_object.py +0 -0
  22. {modal-0.74.44 → modal-0.74.46}/modal/_output.py +0 -0
  23. {modal-0.74.44 → modal-0.74.46}/modal/_partial_function.py +0 -0
  24. {modal-0.74.44 → modal-0.74.46}/modal/_pty.py +0 -0
  25. {modal-0.74.44 → modal-0.74.46}/modal/_resolver.py +0 -0
  26. {modal-0.74.44 → modal-0.74.46}/modal/_resources.py +0 -0
  27. {modal-0.74.44 → modal-0.74.46}/modal/_runtime/__init__.py +0 -0
  28. {modal-0.74.44 → modal-0.74.46}/modal/_runtime/asgi.py +0 -0
  29. {modal-0.74.44 → modal-0.74.46}/modal/_runtime/container_io_manager.py +0 -0
  30. {modal-0.74.44 → modal-0.74.46}/modal/_runtime/container_io_manager.pyi +0 -0
  31. {modal-0.74.44 → modal-0.74.46}/modal/_runtime/execution_context.py +0 -0
  32. {modal-0.74.44 → modal-0.74.46}/modal/_runtime/execution_context.pyi +0 -0
  33. {modal-0.74.44 → modal-0.74.46}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  34. {modal-0.74.44 → modal-0.74.46}/modal/_runtime/telemetry.py +0 -0
  35. {modal-0.74.44 → modal-0.74.46}/modal/_runtime/user_code_imports.py +0 -0
  36. {modal-0.74.44 → modal-0.74.46}/modal/_serialization.py +0 -0
  37. {modal-0.74.44 → modal-0.74.46}/modal/_traceback.py +0 -0
  38. {modal-0.74.44 → modal-0.74.46}/modal/_tunnel.py +0 -0
  39. {modal-0.74.44 → modal-0.74.46}/modal/_tunnel.pyi +0 -0
  40. {modal-0.74.44 → modal-0.74.46}/modal/_type_manager.py +0 -0
  41. {modal-0.74.44 → modal-0.74.46}/modal/_utils/__init__.py +0 -0
  42. {modal-0.74.44 → modal-0.74.46}/modal/_utils/app_utils.py +0 -0
  43. {modal-0.74.44 → modal-0.74.46}/modal/_utils/async_utils.py +0 -0
  44. {modal-0.74.44 → modal-0.74.46}/modal/_utils/blob_utils.py +0 -0
  45. {modal-0.74.44 → modal-0.74.46}/modal/_utils/bytes_io_segment_payload.py +0 -0
  46. {modal-0.74.44 → modal-0.74.46}/modal/_utils/deprecation.py +0 -0
  47. {modal-0.74.44 → modal-0.74.46}/modal/_utils/docker_utils.py +0 -0
  48. {modal-0.74.44 → modal-0.74.46}/modal/_utils/function_utils.py +0 -0
  49. {modal-0.74.44 → modal-0.74.46}/modal/_utils/git_utils.py +0 -0
  50. {modal-0.74.44 → modal-0.74.46}/modal/_utils/grpc_testing.py +0 -0
  51. {modal-0.74.44 → modal-0.74.46}/modal/_utils/grpc_utils.py +0 -0
  52. {modal-0.74.44 → modal-0.74.46}/modal/_utils/hash_utils.py +0 -0
  53. {modal-0.74.44 → modal-0.74.46}/modal/_utils/http_utils.py +0 -0
  54. {modal-0.74.44 → modal-0.74.46}/modal/_utils/jwt_utils.py +0 -0
  55. {modal-0.74.44 → modal-0.74.46}/modal/_utils/logger.py +0 -0
  56. {modal-0.74.44 → modal-0.74.46}/modal/_utils/mount_utils.py +0 -0
  57. {modal-0.74.44 → modal-0.74.46}/modal/_utils/name_utils.py +0 -0
  58. {modal-0.74.44 → modal-0.74.46}/modal/_utils/package_utils.py +0 -0
  59. {modal-0.74.44 → modal-0.74.46}/modal/_utils/pattern_utils.py +0 -0
  60. {modal-0.74.44 → modal-0.74.46}/modal/_utils/rand_pb_testing.py +0 -0
  61. {modal-0.74.44 → modal-0.74.46}/modal/_utils/shell_utils.py +0 -0
  62. {modal-0.74.44 → modal-0.74.46}/modal/_vendor/__init__.py +0 -0
  63. {modal-0.74.44 → modal-0.74.46}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  64. {modal-0.74.44 → modal-0.74.46}/modal/_vendor/cloudpickle.py +0 -0
  65. {modal-0.74.44 → modal-0.74.46}/modal/_vendor/tblib.py +0 -0
  66. {modal-0.74.44 → modal-0.74.46}/modal/_watcher.py +0 -0
  67. {modal-0.74.44 → modal-0.74.46}/modal/app.py +0 -0
  68. {modal-0.74.44 → modal-0.74.46}/modal/app.pyi +0 -0
  69. {modal-0.74.44 → modal-0.74.46}/modal/call_graph.py +0 -0
  70. {modal-0.74.44 → modal-0.74.46}/modal/cli/__init__.py +0 -0
  71. {modal-0.74.44 → modal-0.74.46}/modal/cli/_download.py +0 -0
  72. {modal-0.74.44 → modal-0.74.46}/modal/cli/_traceback.py +0 -0
  73. {modal-0.74.44 → modal-0.74.46}/modal/cli/app.py +0 -0
  74. {modal-0.74.44 → modal-0.74.46}/modal/cli/config.py +0 -0
  75. {modal-0.74.44 → modal-0.74.46}/modal/cli/container.py +0 -0
  76. {modal-0.74.44 → modal-0.74.46}/modal/cli/dict.py +0 -0
  77. {modal-0.74.44 → modal-0.74.46}/modal/cli/entry_point.py +0 -0
  78. {modal-0.74.44 → modal-0.74.46}/modal/cli/environment.py +0 -0
  79. {modal-0.74.44 → modal-0.74.46}/modal/cli/import_refs.py +0 -0
  80. {modal-0.74.44 → modal-0.74.46}/modal/cli/launch.py +0 -0
  81. {modal-0.74.44 → modal-0.74.46}/modal/cli/network_file_system.py +0 -0
  82. {modal-0.74.44 → modal-0.74.46}/modal/cli/profile.py +0 -0
  83. {modal-0.74.44 → modal-0.74.46}/modal/cli/programs/__init__.py +0 -0
  84. {modal-0.74.44 → modal-0.74.46}/modal/cli/programs/run_jupyter.py +0 -0
  85. {modal-0.74.44 → modal-0.74.46}/modal/cli/programs/vscode.py +0 -0
  86. {modal-0.74.44 → modal-0.74.46}/modal/cli/queues.py +0 -0
  87. {modal-0.74.44 → modal-0.74.46}/modal/cli/run.py +0 -0
  88. {modal-0.74.44 → modal-0.74.46}/modal/cli/secret.py +0 -0
  89. {modal-0.74.44 → modal-0.74.46}/modal/cli/token.py +0 -0
  90. {modal-0.74.44 → modal-0.74.46}/modal/cli/utils.py +0 -0
  91. {modal-0.74.44 → modal-0.74.46}/modal/cli/volume.py +0 -0
  92. {modal-0.74.44 → modal-0.74.46}/modal/client.py +0 -0
  93. {modal-0.74.44 → modal-0.74.46}/modal/cloud_bucket_mount.py +0 -0
  94. {modal-0.74.44 → modal-0.74.46}/modal/cloud_bucket_mount.pyi +0 -0
  95. {modal-0.74.44 → modal-0.74.46}/modal/config.py +0 -0
  96. {modal-0.74.44 → modal-0.74.46}/modal/container_process.py +0 -0
  97. {modal-0.74.44 → modal-0.74.46}/modal/container_process.pyi +0 -0
  98. {modal-0.74.44 → modal-0.74.46}/modal/dict.py +0 -0
  99. {modal-0.74.44 → modal-0.74.46}/modal/dict.pyi +0 -0
  100. {modal-0.74.44 → modal-0.74.46}/modal/environments.py +0 -0
  101. {modal-0.74.44 → modal-0.74.46}/modal/environments.pyi +0 -0
  102. {modal-0.74.44 → modal-0.74.46}/modal/exception.py +0 -0
  103. {modal-0.74.44 → modal-0.74.46}/modal/experimental/ipython.py +0 -0
  104. {modal-0.74.44 → modal-0.74.46}/modal/file_io.py +0 -0
  105. {modal-0.74.44 → modal-0.74.46}/modal/file_io.pyi +0 -0
  106. {modal-0.74.44 → modal-0.74.46}/modal/file_pattern_matcher.py +0 -0
  107. {modal-0.74.44 → modal-0.74.46}/modal/functions.py +0 -0
  108. {modal-0.74.44 → modal-0.74.46}/modal/gpu.py +0 -0
  109. {modal-0.74.44 → modal-0.74.46}/modal/image.py +0 -0
  110. {modal-0.74.44 → modal-0.74.46}/modal/image.pyi +0 -0
  111. {modal-0.74.44 → modal-0.74.46}/modal/io_streams.py +0 -0
  112. {modal-0.74.44 → modal-0.74.46}/modal/io_streams.pyi +0 -0
  113. {modal-0.74.44 → modal-0.74.46}/modal/mount.py +0 -0
  114. {modal-0.74.44 → modal-0.74.46}/modal/mount.pyi +0 -0
  115. {modal-0.74.44 → modal-0.74.46}/modal/network_file_system.py +0 -0
  116. {modal-0.74.44 → modal-0.74.46}/modal/network_file_system.pyi +0 -0
  117. {modal-0.74.44 → modal-0.74.46}/modal/object.py +0 -0
  118. {modal-0.74.44 → modal-0.74.46}/modal/object.pyi +0 -0
  119. {modal-0.74.44 → modal-0.74.46}/modal/output.py +0 -0
  120. {modal-0.74.44 → modal-0.74.46}/modal/parallel_map.py +0 -0
  121. {modal-0.74.44 → modal-0.74.46}/modal/parallel_map.pyi +0 -0
  122. {modal-0.74.44 → modal-0.74.46}/modal/partial_function.py +0 -0
  123. {modal-0.74.44 → modal-0.74.46}/modal/partial_function.pyi +0 -0
  124. {modal-0.74.44 → modal-0.74.46}/modal/proxy.py +0 -0
  125. {modal-0.74.44 → modal-0.74.46}/modal/proxy.pyi +0 -0
  126. {modal-0.74.44 → modal-0.74.46}/modal/py.typed +0 -0
  127. {modal-0.74.44 → modal-0.74.46}/modal/queue.py +0 -0
  128. {modal-0.74.44 → modal-0.74.46}/modal/queue.pyi +0 -0
  129. {modal-0.74.44 → modal-0.74.46}/modal/requirements/2023.12.312.txt +0 -0
  130. {modal-0.74.44 → modal-0.74.46}/modal/requirements/2023.12.txt +0 -0
  131. {modal-0.74.44 → modal-0.74.46}/modal/requirements/2024.04.txt +0 -0
  132. {modal-0.74.44 → modal-0.74.46}/modal/requirements/2024.10.txt +0 -0
  133. {modal-0.74.44 → modal-0.74.46}/modal/requirements/PREVIEW.txt +0 -0
  134. {modal-0.74.44 → modal-0.74.46}/modal/requirements/README.md +0 -0
  135. {modal-0.74.44 → modal-0.74.46}/modal/requirements/base-images.json +0 -0
  136. {modal-0.74.44 → modal-0.74.46}/modal/retries.py +0 -0
  137. {modal-0.74.44 → modal-0.74.46}/modal/runner.py +0 -0
  138. {modal-0.74.44 → modal-0.74.46}/modal/runner.pyi +0 -0
  139. {modal-0.74.44 → modal-0.74.46}/modal/running_app.py +0 -0
  140. {modal-0.74.44 → modal-0.74.46}/modal/sandbox.py +0 -0
  141. {modal-0.74.44 → modal-0.74.46}/modal/sandbox.pyi +0 -0
  142. {modal-0.74.44 → modal-0.74.46}/modal/schedule.py +0 -0
  143. {modal-0.74.44 → modal-0.74.46}/modal/scheduler_placement.py +0 -0
  144. {modal-0.74.44 → modal-0.74.46}/modal/serving.py +0 -0
  145. {modal-0.74.44 → modal-0.74.46}/modal/serving.pyi +0 -0
  146. {modal-0.74.44 → modal-0.74.46}/modal/snapshot.py +0 -0
  147. {modal-0.74.44 → modal-0.74.46}/modal/snapshot.pyi +0 -0
  148. {modal-0.74.44 → modal-0.74.46}/modal/stream_type.py +0 -0
  149. {modal-0.74.44 → modal-0.74.46}/modal/token_flow.py +0 -0
  150. {modal-0.74.44 → modal-0.74.46}/modal/token_flow.pyi +0 -0
  151. {modal-0.74.44 → modal-0.74.46}/modal/volume.py +0 -0
  152. {modal-0.74.44 → modal-0.74.46}/modal/volume.pyi +0 -0
  153. {modal-0.74.44 → modal-0.74.46}/modal.egg-info/SOURCES.txt +0 -0
  154. {modal-0.74.44 → modal-0.74.46}/modal.egg-info/dependency_links.txt +0 -0
  155. {modal-0.74.44 → modal-0.74.46}/modal.egg-info/entry_points.txt +0 -0
  156. {modal-0.74.44 → modal-0.74.46}/modal.egg-info/requires.txt +0 -0
  157. {modal-0.74.44 → modal-0.74.46}/modal.egg-info/top_level.txt +0 -0
  158. {modal-0.74.44 → modal-0.74.46}/modal_docs/__init__.py +0 -0
  159. {modal-0.74.44 → modal-0.74.46}/modal_docs/gen_cli_docs.py +0 -0
  160. {modal-0.74.44 → modal-0.74.46}/modal_docs/gen_reference_docs.py +0 -0
  161. {modal-0.74.44 → modal-0.74.46}/modal_docs/mdmd/__init__.py +0 -0
  162. {modal-0.74.44 → modal-0.74.46}/modal_docs/mdmd/mdmd.py +0 -0
  163. {modal-0.74.44 → modal-0.74.46}/modal_docs/mdmd/signatures.py +0 -0
  164. {modal-0.74.44 → modal-0.74.46}/modal_proto/__init__.py +0 -0
  165. {modal-0.74.44 → modal-0.74.46}/modal_proto/api.proto +0 -0
  166. {modal-0.74.44 → modal-0.74.46}/modal_proto/api_grpc.py +0 -0
  167. {modal-0.74.44 → modal-0.74.46}/modal_proto/api_pb2.py +0 -0
  168. {modal-0.74.44 → modal-0.74.46}/modal_proto/api_pb2.pyi +0 -0
  169. {modal-0.74.44 → modal-0.74.46}/modal_proto/api_pb2_grpc.py +0 -0
  170. {modal-0.74.44 → modal-0.74.46}/modal_proto/api_pb2_grpc.pyi +0 -0
  171. {modal-0.74.44 → modal-0.74.46}/modal_proto/modal_api_grpc.py +0 -0
  172. {modal-0.74.44 → modal-0.74.46}/modal_proto/modal_options_grpc.py +0 -0
  173. {modal-0.74.44 → modal-0.74.46}/modal_proto/options.proto +0 -0
  174. {modal-0.74.44 → modal-0.74.46}/modal_proto/options_grpc.py +0 -0
  175. {modal-0.74.44 → modal-0.74.46}/modal_proto/options_pb2.py +0 -0
  176. {modal-0.74.44 → modal-0.74.46}/modal_proto/options_pb2.pyi +0 -0
  177. {modal-0.74.44 → modal-0.74.46}/modal_proto/options_pb2_grpc.py +0 -0
  178. {modal-0.74.44 → modal-0.74.46}/modal_proto/options_pb2_grpc.pyi +0 -0
  179. {modal-0.74.44 → modal-0.74.46}/modal_proto/py.typed +0 -0
  180. {modal-0.74.44 → modal-0.74.46}/modal_version/__init__.py +0 -0
  181. {modal-0.74.44 → modal-0.74.46}/modal_version/__main__.py +0 -0
  182. {modal-0.74.44 → modal-0.74.46}/pyproject.toml +0 -0
  183. {modal-0.74.44 → modal-0.74.46}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 0.74.44
3
+ Version: 0.74.46
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -1049,21 +1049,69 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1049
1049
  fun._spec = self._spec # TODO (elias): fix - this is incorrect when using with_options
1050
1050
  return fun
1051
1051
 
1052
+ @live_method
1053
+ async def update_autoscaler(
1054
+ self,
1055
+ *,
1056
+ min_containers: Optional[int] = None,
1057
+ max_containers: Optional[int] = None,
1058
+ buffer_containers: Optional[int] = None,
1059
+ scaledown_window: Optional[int] = None,
1060
+ ) -> None:
1061
+ """Override the current autoscaler behavior for this Function.
1062
+
1063
+ Unspecified parameters will retain their current value, i.e. either the static value
1064
+ from the function decorator, or an override value from a previous call to this method.
1065
+
1066
+ Subsequent deployments of the App containing this Function will reset the autoscaler back to
1067
+ its static configuration.
1068
+
1069
+ Examples:
1070
+
1071
+ ```python notest
1072
+ f = modal.Function.from_name("my-app", "function")
1073
+
1074
+ # Always have at least 2 containers running, with an extra buffer when the Function is active
1075
+ f.update_autoscaler(min_containers=2, buffer_containers=1)
1076
+
1077
+ # Limit this Function to avoid spinning up more than 5 containers
1078
+ f.update_autoscaler(max_containers=5)
1079
+
1080
+ # Extend the scaledown window to increase the amount of time that idle containers stay alive
1081
+ f.update_autoscaler(scaledown_window=300)
1082
+
1083
+ ```
1084
+
1085
+ """
1086
+ if self._is_method:
1087
+ raise InvalidError("Cannot call .update_autoscaler() on a method. Call it on the class instance instead.")
1088
+
1089
+ settings = api_pb2.AutoscalerSettings(
1090
+ min_containers=min_containers,
1091
+ max_containers=max_containers,
1092
+ buffer_containers=buffer_containers,
1093
+ scaledown_window=scaledown_window,
1094
+ )
1095
+ request = api_pb2.FunctionUpdateSchedulingParamsRequest(function_id=self.object_id, settings=settings)
1096
+ await retry_transient_errors(self.client.stub.FunctionUpdateSchedulingParams, request)
1097
+
1098
+ # One idea would be for FunctionUpdateScheduleParams to return the current (coalesced) settings
1099
+ # and then we could return them here (would need some ad hoc dataclass, which I don't love)
1100
+
1052
1101
  @live_method
1053
1102
  async def keep_warm(self, warm_pool_size: int) -> None:
1054
- """Set the warm pool size for the function.
1103
+ """Set the warm pool size for the Function.
1055
1104
 
1056
- Please exercise care when using this advanced feature!
1057
- Setting and forgetting a warm pool on functions can lead to increased costs.
1105
+ DEPRECATED: Please adapt your code to use the more general `update_autoscaler` method instead:
1058
1106
 
1059
1107
  ```python notest
1060
- # Usage on a regular function.
1061
1108
  f = modal.Function.from_name("my-app", "function")
1109
+
1110
+ # Old pattern (deprecated)
1062
1111
  f.keep_warm(2)
1063
1112
 
1064
- # Usage on a parametrized function.
1065
- Model = modal.Cls.from_name("my-app", "Model")
1066
- Model("fine-tuned-model").keep_warm(2) # note that this applies to the class instance, not a method
1113
+ # New pattern
1114
+ f.update_autoscaler(min_containers=2)
1067
1115
  ```
1068
1116
  """
1069
1117
  if self._is_method:
@@ -1077,10 +1125,14 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1077
1125
  """
1078
1126
  )
1079
1127
  )
1080
- request = api_pb2.FunctionUpdateSchedulingParamsRequest(
1081
- function_id=self.object_id, warm_pool_size_override=warm_pool_size
1128
+
1129
+ deprecation_warning(
1130
+ (2025, 5, 5),
1131
+ "The .keep_warm() method has been deprecated in favor of the more general "
1132
+ ".update_autoscaler(min_containers=...) method.",
1133
+ show_source=True,
1082
1134
  )
1083
- await retry_transient_errors(self.client.stub.FunctionUpdateSchedulingParams, request)
1135
+ await self.update_autoscaler(min_containers=warm_pool_size)
1084
1136
 
1085
1137
  @classmethod
1086
1138
  def _from_name(cls, app_name: str, name: str, namespace, environment_name: Optional[str]):
@@ -27,7 +27,7 @@ class _Client:
27
27
  _snapshotted: bool
28
28
 
29
29
  def __init__(
30
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.44"
30
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.46"
31
31
  ): ...
32
32
  def is_closed(self) -> bool: ...
33
33
  @property
@@ -85,7 +85,7 @@ class Client:
85
85
  _snapshotted: bool
86
86
 
87
87
  def __init__(
88
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.44"
88
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.46"
89
89
  ): ...
90
90
  def is_closed(self) -> bool: ...
91
91
  @property
@@ -231,22 +231,70 @@ class _Obj:
231
231
  user_cls_instance._modal_functions = instance_methods
232
232
  return user_cls_instance
233
233
 
234
+ async def update_autoscaler(
235
+ self,
236
+ *,
237
+ min_containers: Optional[int] = None,
238
+ max_containers: Optional[int] = None,
239
+ scaledown_window: Optional[int] = None,
240
+ buffer_containers: Optional[int] = None,
241
+ ) -> None:
242
+ """Override the current autoscaler behavior for this Cls instance.
243
+
244
+ Unspecified parameters will retain their current value, i.e. either the static value
245
+ from the function decorator, or an override value from a previous call to this method.
246
+
247
+ Subsequent deployments of the App containing this Cls will reset the autoscaler back to
248
+ its static configuration.
249
+
250
+ Note: When calling this method on a Cls that is defined locally, static type checkers will
251
+ issue an error, because the object will appear to have the user-defined type.
252
+
253
+ Examples:
254
+
255
+ ```python notest
256
+ Model = modal.Cls.from_name("my-app", "Model")
257
+ model = Model() # This method is called on an *instance* of the class
258
+
259
+ # Always have at least 2 containers running, with an extra buffer when the Function is active
260
+ model.update_autoscaler(min_containers=2, buffer_containers=1)
261
+
262
+ # Limit this Function to avoid spinning up more than 5 containers
263
+ f.update_autoscaler(max_containers=5)
264
+ ```
265
+
266
+ """
267
+ return await self._cached_service_function().update_autoscaler(
268
+ min_containers=min_containers,
269
+ max_containers=max_containers,
270
+ scaledown_window=scaledown_window,
271
+ buffer_containers=buffer_containers,
272
+ )
273
+
234
274
  async def keep_warm(self, warm_pool_size: int) -> None:
235
275
  """Set the warm pool size for the class containers
236
276
 
237
- Please exercise care when using this advanced feature!
238
- Setting and forgetting a warm pool on functions can lead to increased costs.
239
-
240
- Note that all Modal methods and web endpoints of a class share the same set
241
- of containers and the warm_pool_size affects that common container pool.
277
+ DEPRECATED: Please adapt your code to use the more general `update_autoscaler` method instead:
242
278
 
243
279
  ```python notest
244
- # Usage on a parametrized function.
245
280
  Model = modal.Cls.from_name("my-app", "Model")
246
- Model("fine-tuned-model").keep_warm(2)
281
+ model = Model() # This method is called on an *instance* of the class
282
+
283
+ # Old pattern (deprecated)
284
+ model.keep_warm(2)
285
+
286
+ # New pattern
287
+ model.update_autoscaler(min_containers=2)
247
288
  ```
289
+
248
290
  """
249
- await self._cached_service_function().keep_warm(warm_pool_size)
291
+ deprecation_warning(
292
+ (2025, 5, 5),
293
+ "The .keep_warm() method has been deprecated in favor of the more general "
294
+ ".update_autoscaler(min_containers=...) method.",
295
+ show_source=True,
296
+ )
297
+ await self._cached_service_function().update_autoscaler(min_containers=warm_pool_size)
250
298
 
251
299
  def _cached_user_cls_instance(self):
252
300
  """Get or construct the local object
@@ -65,6 +65,14 @@ class _Obj:
65
65
  def _cached_service_function(self) -> modal._functions._Function: ...
66
66
  def _get_parameter_values(self) -> dict[str, typing.Any]: ...
67
67
  def _new_user_cls_instance(self): ...
68
+ async def update_autoscaler(
69
+ self,
70
+ *,
71
+ min_containers: typing.Optional[int] = None,
72
+ max_containers: typing.Optional[int] = None,
73
+ scaledown_window: typing.Optional[int] = None,
74
+ buffer_containers: typing.Optional[int] = None,
75
+ ) -> None: ...
68
76
  async def keep_warm(self, warm_pool_size: int) -> None: ...
69
77
  def _cached_user_cls_instance(self): ...
70
78
  def _enter(self): ...
@@ -94,6 +102,26 @@ class Obj:
94
102
  def _get_parameter_values(self) -> dict[str, typing.Any]: ...
95
103
  def _new_user_cls_instance(self): ...
96
104
 
105
+ class __update_autoscaler_spec(typing_extensions.Protocol[SUPERSELF]):
106
+ def __call__(
107
+ self,
108
+ *,
109
+ min_containers: typing.Optional[int] = None,
110
+ max_containers: typing.Optional[int] = None,
111
+ scaledown_window: typing.Optional[int] = None,
112
+ buffer_containers: typing.Optional[int] = None,
113
+ ) -> None: ...
114
+ async def aio(
115
+ self,
116
+ *,
117
+ min_containers: typing.Optional[int] = None,
118
+ max_containers: typing.Optional[int] = None,
119
+ scaledown_window: typing.Optional[int] = None,
120
+ buffer_containers: typing.Optional[int] = None,
121
+ ) -> None: ...
122
+
123
+ update_autoscaler: __update_autoscaler_spec[typing_extensions.Self]
124
+
97
125
  class __keep_warm_spec(typing_extensions.Protocol[SUPERSELF]):
98
126
  def __call__(self, warm_pool_size: int) -> None: ...
99
127
  async def aio(self, warm_pool_size: int) -> None: ...
@@ -12,6 +12,7 @@ from .._object import _get_environment_name
12
12
  from .._partial_function import _clustered
13
13
  from .._runtime.container_io_manager import _ContainerIOManager
14
14
  from .._utils.async_utils import synchronize_api, synchronizer
15
+ from .._utils.deprecation import deprecation_warning
15
16
  from .._utils.grpc_utils import retry_transient_errors
16
17
  from ..client import _Client
17
18
  from ..cls import _Obj
@@ -179,6 +180,13 @@ async def update_autoscaler(
179
180
  may look different (i.e., it may be a standalone function or a method).
180
181
 
181
182
  """
183
+ deprecation_warning(
184
+ (2025, 5, 5),
185
+ "The modal.experimental.update_autoscaler(...) function is now deprecated in favor of"
186
+ " a stable `.update_autoscaler(...) method on the corresponding object.",
187
+ show_source=True,
188
+ )
189
+
182
190
  settings = api_pb2.AutoscalerSettings(
183
191
  min_containers=min_containers,
184
192
  max_containers=max_containers,
@@ -110,6 +110,26 @@ class Function(
110
110
  kwargs: dict[str, typing.Any],
111
111
  ) -> Function: ...
112
112
 
113
+ class __update_autoscaler_spec(typing_extensions.Protocol[SUPERSELF]):
114
+ def __call__(
115
+ self,
116
+ *,
117
+ min_containers: typing.Optional[int] = None,
118
+ max_containers: typing.Optional[int] = None,
119
+ buffer_containers: typing.Optional[int] = None,
120
+ scaledown_window: typing.Optional[int] = None,
121
+ ) -> None: ...
122
+ async def aio(
123
+ self,
124
+ *,
125
+ min_containers: typing.Optional[int] = None,
126
+ max_containers: typing.Optional[int] = None,
127
+ buffer_containers: typing.Optional[int] = None,
128
+ scaledown_window: typing.Optional[int] = None,
129
+ ) -> None: ...
130
+
131
+ update_autoscaler: __update_autoscaler_spec[typing_extensions.Self]
132
+
113
133
  class __keep_warm_spec(typing_extensions.Protocol[SUPERSELF]):
114
134
  def __call__(self, warm_pool_size: int) -> None: ...
115
135
  async def aio(self, warm_pool_size: int) -> None: ...
@@ -34,7 +34,7 @@ class _Secret(_Object, type_prefix="st"):
34
34
  env_dict: dict[
35
35
  str, Union[str, None]
36
36
  ] = {}, # dict of entries to be inserted as environment variables in functions using the secret
37
- ):
37
+ ) -> "_Secret":
38
38
  """Create a secret from a str-str dictionary. Values can also be `None`, which is ignored.
39
39
 
40
40
  Usage:
@@ -81,7 +81,7 @@ class _Secret(_Object, type_prefix="st"):
81
81
  @staticmethod
82
82
  def from_local_environ(
83
83
  env_keys: list[str], # list of local env vars to be included for remote execution
84
- ):
84
+ ) -> "_Secret":
85
85
  """Create secrets from local environment variables automatically."""
86
86
 
87
87
  if is_local():
@@ -96,7 +96,7 @@ class _Secret(_Object, type_prefix="st"):
96
96
  return _Secret.from_dict({})
97
97
 
98
98
  @staticmethod
99
- def from_dotenv(path=None, *, filename=".env"):
99
+ def from_dotenv(path=None, *, filename=".env") -> "_Secret":
100
100
  """Create secrets from a .env file automatically.
101
101
 
102
102
  If no argument is provided, it will use the current working directory as the starting
@@ -6,11 +6,11 @@ import typing_extensions
6
6
 
7
7
  class _Secret(modal._object._Object):
8
8
  @staticmethod
9
- def from_dict(env_dict: dict[str, typing.Optional[str]] = {}): ...
9
+ def from_dict(env_dict: dict[str, typing.Optional[str]] = {}) -> _Secret: ...
10
10
  @staticmethod
11
- def from_local_environ(env_keys: list[str]): ...
11
+ def from_local_environ(env_keys: list[str]) -> _Secret: ...
12
12
  @staticmethod
13
- def from_dotenv(path=None, *, filename=".env"): ...
13
+ def from_dotenv(path=None, *, filename=".env") -> _Secret: ...
14
14
  @staticmethod
15
15
  def from_name(
16
16
  name: str, *, namespace=1, environment_name: typing.Optional[str] = None, required_keys: list[str] = []
@@ -36,11 +36,11 @@ class _Secret(modal._object._Object):
36
36
  class Secret(modal.object.Object):
37
37
  def __init__(self, *args, **kwargs): ...
38
38
  @staticmethod
39
- def from_dict(env_dict: dict[str, typing.Optional[str]] = {}): ...
39
+ def from_dict(env_dict: dict[str, typing.Optional[str]] = {}) -> Secret: ...
40
40
  @staticmethod
41
- def from_local_environ(env_keys: list[str]): ...
41
+ def from_local_environ(env_keys: list[str]) -> Secret: ...
42
42
  @staticmethod
43
- def from_dotenv(path=None, *, filename=".env"): ...
43
+ def from_dotenv(path=None, *, filename=".env") -> Secret: ...
44
44
  @staticmethod
45
45
  def from_name(
46
46
  name: str, *, namespace=1, environment_name: typing.Optional[str] = None, required_keys: list[str] = []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 0.74.44
3
+ Version: 0.74.46
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
 
3
3
  # Note: Reset this value to -1 whenever you make a minor `0.X` release of the client.
4
- build_number = 44 # git: 58b258f
4
+ build_number = 46 # git: c9b08ba
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes