modal 0.74.45__tar.gz → 0.74.47__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.45 → modal-0.74.47}/PKG-INFO +1 -1
  2. {modal-0.74.45 → modal-0.74.47}/modal/_functions.py +62 -10
  3. {modal-0.74.45 → modal-0.74.47}/modal/_utils/grpc_utils.py +9 -0
  4. {modal-0.74.45 → modal-0.74.47}/modal/client.pyi +2 -2
  5. {modal-0.74.45 → modal-0.74.47}/modal/cls.py +56 -8
  6. {modal-0.74.45 → modal-0.74.47}/modal/cls.pyi +28 -0
  7. {modal-0.74.45 → modal-0.74.47}/modal/experimental/__init__.py +8 -0
  8. {modal-0.74.45 → modal-0.74.47}/modal/functions.pyi +26 -6
  9. {modal-0.74.45 → modal-0.74.47}/modal.egg-info/PKG-INFO +1 -1
  10. {modal-0.74.45 → modal-0.74.47}/modal_version/_version_generated.py +1 -1
  11. {modal-0.74.45 → modal-0.74.47}/LICENSE +0 -0
  12. {modal-0.74.45 → modal-0.74.47}/README.md +0 -0
  13. {modal-0.74.45 → modal-0.74.47}/modal/__init__.py +0 -0
  14. {modal-0.74.45 → modal-0.74.47}/modal/__main__.py +0 -0
  15. {modal-0.74.45 → modal-0.74.47}/modal/_clustered_functions.py +0 -0
  16. {modal-0.74.45 → modal-0.74.47}/modal/_clustered_functions.pyi +0 -0
  17. {modal-0.74.45 → modal-0.74.47}/modal/_container_entrypoint.py +0 -0
  18. {modal-0.74.45 → modal-0.74.47}/modal/_ipython.py +0 -0
  19. {modal-0.74.45 → modal-0.74.47}/modal/_location.py +0 -0
  20. {modal-0.74.45 → modal-0.74.47}/modal/_object.py +0 -0
  21. {modal-0.74.45 → modal-0.74.47}/modal/_output.py +0 -0
  22. {modal-0.74.45 → modal-0.74.47}/modal/_partial_function.py +0 -0
  23. {modal-0.74.45 → modal-0.74.47}/modal/_pty.py +0 -0
  24. {modal-0.74.45 → modal-0.74.47}/modal/_resolver.py +0 -0
  25. {modal-0.74.45 → modal-0.74.47}/modal/_resources.py +0 -0
  26. {modal-0.74.45 → modal-0.74.47}/modal/_runtime/__init__.py +0 -0
  27. {modal-0.74.45 → modal-0.74.47}/modal/_runtime/asgi.py +0 -0
  28. {modal-0.74.45 → modal-0.74.47}/modal/_runtime/container_io_manager.py +0 -0
  29. {modal-0.74.45 → modal-0.74.47}/modal/_runtime/container_io_manager.pyi +0 -0
  30. {modal-0.74.45 → modal-0.74.47}/modal/_runtime/execution_context.py +0 -0
  31. {modal-0.74.45 → modal-0.74.47}/modal/_runtime/execution_context.pyi +0 -0
  32. {modal-0.74.45 → modal-0.74.47}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  33. {modal-0.74.45 → modal-0.74.47}/modal/_runtime/telemetry.py +0 -0
  34. {modal-0.74.45 → modal-0.74.47}/modal/_runtime/user_code_imports.py +0 -0
  35. {modal-0.74.45 → modal-0.74.47}/modal/_serialization.py +0 -0
  36. {modal-0.74.45 → modal-0.74.47}/modal/_traceback.py +0 -0
  37. {modal-0.74.45 → modal-0.74.47}/modal/_tunnel.py +0 -0
  38. {modal-0.74.45 → modal-0.74.47}/modal/_tunnel.pyi +0 -0
  39. {modal-0.74.45 → modal-0.74.47}/modal/_type_manager.py +0 -0
  40. {modal-0.74.45 → modal-0.74.47}/modal/_utils/__init__.py +0 -0
  41. {modal-0.74.45 → modal-0.74.47}/modal/_utils/app_utils.py +0 -0
  42. {modal-0.74.45 → modal-0.74.47}/modal/_utils/async_utils.py +0 -0
  43. {modal-0.74.45 → modal-0.74.47}/modal/_utils/blob_utils.py +0 -0
  44. {modal-0.74.45 → modal-0.74.47}/modal/_utils/bytes_io_segment_payload.py +0 -0
  45. {modal-0.74.45 → modal-0.74.47}/modal/_utils/deprecation.py +0 -0
  46. {modal-0.74.45 → modal-0.74.47}/modal/_utils/docker_utils.py +0 -0
  47. {modal-0.74.45 → modal-0.74.47}/modal/_utils/function_utils.py +0 -0
  48. {modal-0.74.45 → modal-0.74.47}/modal/_utils/git_utils.py +0 -0
  49. {modal-0.74.45 → modal-0.74.47}/modal/_utils/grpc_testing.py +0 -0
  50. {modal-0.74.45 → modal-0.74.47}/modal/_utils/hash_utils.py +0 -0
  51. {modal-0.74.45 → modal-0.74.47}/modal/_utils/http_utils.py +0 -0
  52. {modal-0.74.45 → modal-0.74.47}/modal/_utils/jwt_utils.py +0 -0
  53. {modal-0.74.45 → modal-0.74.47}/modal/_utils/logger.py +0 -0
  54. {modal-0.74.45 → modal-0.74.47}/modal/_utils/mount_utils.py +0 -0
  55. {modal-0.74.45 → modal-0.74.47}/modal/_utils/name_utils.py +0 -0
  56. {modal-0.74.45 → modal-0.74.47}/modal/_utils/package_utils.py +0 -0
  57. {modal-0.74.45 → modal-0.74.47}/modal/_utils/pattern_utils.py +0 -0
  58. {modal-0.74.45 → modal-0.74.47}/modal/_utils/rand_pb_testing.py +0 -0
  59. {modal-0.74.45 → modal-0.74.47}/modal/_utils/shell_utils.py +0 -0
  60. {modal-0.74.45 → modal-0.74.47}/modal/_vendor/__init__.py +0 -0
  61. {modal-0.74.45 → modal-0.74.47}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  62. {modal-0.74.45 → modal-0.74.47}/modal/_vendor/cloudpickle.py +0 -0
  63. {modal-0.74.45 → modal-0.74.47}/modal/_vendor/tblib.py +0 -0
  64. {modal-0.74.45 → modal-0.74.47}/modal/_watcher.py +0 -0
  65. {modal-0.74.45 → modal-0.74.47}/modal/app.py +0 -0
  66. {modal-0.74.45 → modal-0.74.47}/modal/app.pyi +0 -0
  67. {modal-0.74.45 → modal-0.74.47}/modal/call_graph.py +0 -0
  68. {modal-0.74.45 → modal-0.74.47}/modal/cli/__init__.py +0 -0
  69. {modal-0.74.45 → modal-0.74.47}/modal/cli/_download.py +0 -0
  70. {modal-0.74.45 → modal-0.74.47}/modal/cli/_traceback.py +0 -0
  71. {modal-0.74.45 → modal-0.74.47}/modal/cli/app.py +0 -0
  72. {modal-0.74.45 → modal-0.74.47}/modal/cli/config.py +0 -0
  73. {modal-0.74.45 → modal-0.74.47}/modal/cli/container.py +0 -0
  74. {modal-0.74.45 → modal-0.74.47}/modal/cli/dict.py +0 -0
  75. {modal-0.74.45 → modal-0.74.47}/modal/cli/entry_point.py +0 -0
  76. {modal-0.74.45 → modal-0.74.47}/modal/cli/environment.py +0 -0
  77. {modal-0.74.45 → modal-0.74.47}/modal/cli/import_refs.py +0 -0
  78. {modal-0.74.45 → modal-0.74.47}/modal/cli/launch.py +0 -0
  79. {modal-0.74.45 → modal-0.74.47}/modal/cli/network_file_system.py +0 -0
  80. {modal-0.74.45 → modal-0.74.47}/modal/cli/profile.py +0 -0
  81. {modal-0.74.45 → modal-0.74.47}/modal/cli/programs/__init__.py +0 -0
  82. {modal-0.74.45 → modal-0.74.47}/modal/cli/programs/run_jupyter.py +0 -0
  83. {modal-0.74.45 → modal-0.74.47}/modal/cli/programs/vscode.py +0 -0
  84. {modal-0.74.45 → modal-0.74.47}/modal/cli/queues.py +0 -0
  85. {modal-0.74.45 → modal-0.74.47}/modal/cli/run.py +0 -0
  86. {modal-0.74.45 → modal-0.74.47}/modal/cli/secret.py +0 -0
  87. {modal-0.74.45 → modal-0.74.47}/modal/cli/token.py +0 -0
  88. {modal-0.74.45 → modal-0.74.47}/modal/cli/utils.py +0 -0
  89. {modal-0.74.45 → modal-0.74.47}/modal/cli/volume.py +0 -0
  90. {modal-0.74.45 → modal-0.74.47}/modal/client.py +0 -0
  91. {modal-0.74.45 → modal-0.74.47}/modal/cloud_bucket_mount.py +0 -0
  92. {modal-0.74.45 → modal-0.74.47}/modal/cloud_bucket_mount.pyi +0 -0
  93. {modal-0.74.45 → modal-0.74.47}/modal/config.py +0 -0
  94. {modal-0.74.45 → modal-0.74.47}/modal/container_process.py +0 -0
  95. {modal-0.74.45 → modal-0.74.47}/modal/container_process.pyi +0 -0
  96. {modal-0.74.45 → modal-0.74.47}/modal/dict.py +0 -0
  97. {modal-0.74.45 → modal-0.74.47}/modal/dict.pyi +0 -0
  98. {modal-0.74.45 → modal-0.74.47}/modal/environments.py +0 -0
  99. {modal-0.74.45 → modal-0.74.47}/modal/environments.pyi +0 -0
  100. {modal-0.74.45 → modal-0.74.47}/modal/exception.py +0 -0
  101. {modal-0.74.45 → modal-0.74.47}/modal/experimental/ipython.py +0 -0
  102. {modal-0.74.45 → modal-0.74.47}/modal/file_io.py +0 -0
  103. {modal-0.74.45 → modal-0.74.47}/modal/file_io.pyi +0 -0
  104. {modal-0.74.45 → modal-0.74.47}/modal/file_pattern_matcher.py +0 -0
  105. {modal-0.74.45 → modal-0.74.47}/modal/functions.py +0 -0
  106. {modal-0.74.45 → modal-0.74.47}/modal/gpu.py +0 -0
  107. {modal-0.74.45 → modal-0.74.47}/modal/image.py +0 -0
  108. {modal-0.74.45 → modal-0.74.47}/modal/image.pyi +0 -0
  109. {modal-0.74.45 → modal-0.74.47}/modal/io_streams.py +0 -0
  110. {modal-0.74.45 → modal-0.74.47}/modal/io_streams.pyi +0 -0
  111. {modal-0.74.45 → modal-0.74.47}/modal/mount.py +0 -0
  112. {modal-0.74.45 → modal-0.74.47}/modal/mount.pyi +0 -0
  113. {modal-0.74.45 → modal-0.74.47}/modal/network_file_system.py +0 -0
  114. {modal-0.74.45 → modal-0.74.47}/modal/network_file_system.pyi +0 -0
  115. {modal-0.74.45 → modal-0.74.47}/modal/object.py +0 -0
  116. {modal-0.74.45 → modal-0.74.47}/modal/object.pyi +0 -0
  117. {modal-0.74.45 → modal-0.74.47}/modal/output.py +0 -0
  118. {modal-0.74.45 → modal-0.74.47}/modal/parallel_map.py +0 -0
  119. {modal-0.74.45 → modal-0.74.47}/modal/parallel_map.pyi +0 -0
  120. {modal-0.74.45 → modal-0.74.47}/modal/partial_function.py +0 -0
  121. {modal-0.74.45 → modal-0.74.47}/modal/partial_function.pyi +0 -0
  122. {modal-0.74.45 → modal-0.74.47}/modal/proxy.py +0 -0
  123. {modal-0.74.45 → modal-0.74.47}/modal/proxy.pyi +0 -0
  124. {modal-0.74.45 → modal-0.74.47}/modal/py.typed +0 -0
  125. {modal-0.74.45 → modal-0.74.47}/modal/queue.py +0 -0
  126. {modal-0.74.45 → modal-0.74.47}/modal/queue.pyi +0 -0
  127. {modal-0.74.45 → modal-0.74.47}/modal/requirements/2023.12.312.txt +0 -0
  128. {modal-0.74.45 → modal-0.74.47}/modal/requirements/2023.12.txt +0 -0
  129. {modal-0.74.45 → modal-0.74.47}/modal/requirements/2024.04.txt +0 -0
  130. {modal-0.74.45 → modal-0.74.47}/modal/requirements/2024.10.txt +0 -0
  131. {modal-0.74.45 → modal-0.74.47}/modal/requirements/PREVIEW.txt +0 -0
  132. {modal-0.74.45 → modal-0.74.47}/modal/requirements/README.md +0 -0
  133. {modal-0.74.45 → modal-0.74.47}/modal/requirements/base-images.json +0 -0
  134. {modal-0.74.45 → modal-0.74.47}/modal/retries.py +0 -0
  135. {modal-0.74.45 → modal-0.74.47}/modal/runner.py +0 -0
  136. {modal-0.74.45 → modal-0.74.47}/modal/runner.pyi +0 -0
  137. {modal-0.74.45 → modal-0.74.47}/modal/running_app.py +0 -0
  138. {modal-0.74.45 → modal-0.74.47}/modal/sandbox.py +0 -0
  139. {modal-0.74.45 → modal-0.74.47}/modal/sandbox.pyi +0 -0
  140. {modal-0.74.45 → modal-0.74.47}/modal/schedule.py +0 -0
  141. {modal-0.74.45 → modal-0.74.47}/modal/scheduler_placement.py +0 -0
  142. {modal-0.74.45 → modal-0.74.47}/modal/secret.py +0 -0
  143. {modal-0.74.45 → modal-0.74.47}/modal/secret.pyi +0 -0
  144. {modal-0.74.45 → modal-0.74.47}/modal/serving.py +0 -0
  145. {modal-0.74.45 → modal-0.74.47}/modal/serving.pyi +0 -0
  146. {modal-0.74.45 → modal-0.74.47}/modal/snapshot.py +0 -0
  147. {modal-0.74.45 → modal-0.74.47}/modal/snapshot.pyi +0 -0
  148. {modal-0.74.45 → modal-0.74.47}/modal/stream_type.py +0 -0
  149. {modal-0.74.45 → modal-0.74.47}/modal/token_flow.py +0 -0
  150. {modal-0.74.45 → modal-0.74.47}/modal/token_flow.pyi +0 -0
  151. {modal-0.74.45 → modal-0.74.47}/modal/volume.py +0 -0
  152. {modal-0.74.45 → modal-0.74.47}/modal/volume.pyi +0 -0
  153. {modal-0.74.45 → modal-0.74.47}/modal.egg-info/SOURCES.txt +0 -0
  154. {modal-0.74.45 → modal-0.74.47}/modal.egg-info/dependency_links.txt +0 -0
  155. {modal-0.74.45 → modal-0.74.47}/modal.egg-info/entry_points.txt +0 -0
  156. {modal-0.74.45 → modal-0.74.47}/modal.egg-info/requires.txt +0 -0
  157. {modal-0.74.45 → modal-0.74.47}/modal.egg-info/top_level.txt +0 -0
  158. {modal-0.74.45 → modal-0.74.47}/modal_docs/__init__.py +0 -0
  159. {modal-0.74.45 → modal-0.74.47}/modal_docs/gen_cli_docs.py +0 -0
  160. {modal-0.74.45 → modal-0.74.47}/modal_docs/gen_reference_docs.py +0 -0
  161. {modal-0.74.45 → modal-0.74.47}/modal_docs/mdmd/__init__.py +0 -0
  162. {modal-0.74.45 → modal-0.74.47}/modal_docs/mdmd/mdmd.py +0 -0
  163. {modal-0.74.45 → modal-0.74.47}/modal_docs/mdmd/signatures.py +0 -0
  164. {modal-0.74.45 → modal-0.74.47}/modal_proto/__init__.py +0 -0
  165. {modal-0.74.45 → modal-0.74.47}/modal_proto/api.proto +0 -0
  166. {modal-0.74.45 → modal-0.74.47}/modal_proto/api_grpc.py +0 -0
  167. {modal-0.74.45 → modal-0.74.47}/modal_proto/api_pb2.py +0 -0
  168. {modal-0.74.45 → modal-0.74.47}/modal_proto/api_pb2.pyi +0 -0
  169. {modal-0.74.45 → modal-0.74.47}/modal_proto/api_pb2_grpc.py +0 -0
  170. {modal-0.74.45 → modal-0.74.47}/modal_proto/api_pb2_grpc.pyi +0 -0
  171. {modal-0.74.45 → modal-0.74.47}/modal_proto/modal_api_grpc.py +0 -0
  172. {modal-0.74.45 → modal-0.74.47}/modal_proto/modal_options_grpc.py +0 -0
  173. {modal-0.74.45 → modal-0.74.47}/modal_proto/options.proto +0 -0
  174. {modal-0.74.45 → modal-0.74.47}/modal_proto/options_grpc.py +0 -0
  175. {modal-0.74.45 → modal-0.74.47}/modal_proto/options_pb2.py +0 -0
  176. {modal-0.74.45 → modal-0.74.47}/modal_proto/options_pb2.pyi +0 -0
  177. {modal-0.74.45 → modal-0.74.47}/modal_proto/options_pb2_grpc.py +0 -0
  178. {modal-0.74.45 → modal-0.74.47}/modal_proto/options_pb2_grpc.pyi +0 -0
  179. {modal-0.74.45 → modal-0.74.47}/modal_proto/py.typed +0 -0
  180. {modal-0.74.45 → modal-0.74.47}/modal_version/__init__.py +0 -0
  181. {modal-0.74.45 → modal-0.74.47}/modal_version/__main__.py +0 -0
  182. {modal-0.74.45 → modal-0.74.47}/pyproject.toml +0 -0
  183. {modal-0.74.45 → modal-0.74.47}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 0.74.45
3
+ Version: 0.74.47
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]):
@@ -118,7 +118,16 @@ def create_channel(
118
118
 
119
119
  logger.debug(f"Sending request to {event.method_name}")
120
120
 
121
+ async def recv_trailing_metadata(trailing_metadata: grpclib.events.RecvTrailingMetadata) -> None:
122
+ # If we receive an auth token from the server, include it in all future requests.
123
+ # TODO(nathan): This isn't perfect because the metadata isn't propagated when the
124
+ # process is forked and a new channel is created. This is OK for now since this
125
+ # token is only used by the experimental input plane
126
+ if token := trailing_metadata.metadata.get("x-modal-auth-token"):
127
+ metadata["x-modal-auth-token"] = str(token)
128
+
121
129
  grpclib.events.listen(channel, grpclib.events.SendRequest, send_request)
130
+ grpclib.events.listen(channel, grpclib.events.RecvTrailingMetadata, recv_trailing_metadata)
122
131
 
123
132
  return channel
124
133
 
@@ -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.45"
30
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.47"
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.45"
88
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.47"
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: ...
@@ -198,11 +218,11 @@ class Function(
198
218
 
199
219
  _call_generator_nowait: ___call_generator_nowait_spec[typing_extensions.Self]
200
220
 
201
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
221
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
202
222
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
203
223
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
204
224
 
205
- remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
225
+ remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
206
226
 
207
227
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
208
228
  def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
@@ -217,19 +237,19 @@ class Function(
217
237
  self, *args: modal._functions.P.args, **kwargs: modal._functions.P.kwargs
218
238
  ) -> modal._functions.OriginalReturnType: ...
219
239
 
220
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
240
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
221
241
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
222
242
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
223
243
 
224
244
  _experimental_spawn: ___experimental_spawn_spec[
225
- modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
245
+ modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
226
246
  ]
227
247
 
228
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
248
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
229
249
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
230
250
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
231
251
 
232
- spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
252
+ spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
233
253
 
234
254
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]: ...
235
255
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 0.74.45
3
+ Version: 0.74.47
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 = 45 # git: fe9ccef
4
+ build_number = 47 # git: 8f5251c
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
File without changes
File without changes