modAL 1.3.1.dev25__py3-none-any.whl → 1.3.1.dev27__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/_server.py ADDED
@@ -0,0 +1,246 @@
1
+ # Copyright Modal Labs 2025
2
+ import inspect
3
+ import typing
4
+ from typing import Optional
5
+
6
+ from ._functions import _Function
7
+ from ._load_context import LoadContext
8
+ from ._object import live_method
9
+ from ._partial_function import (
10
+ _find_partial_methods_for_user_cls,
11
+ _PartialFunction,
12
+ _PartialFunctionFlags,
13
+ )
14
+ from .client import _Client
15
+ from .cls import is_parameter
16
+ from .exception import InvalidError
17
+
18
+ if typing.TYPE_CHECKING:
19
+ import modal.app
20
+
21
+
22
+ def validate_http_server_config(
23
+ port: int,
24
+ proxy_regions: list[str], # The regions to proxy the HTTP server to.
25
+ startup_timeout: int, # Maximum number of seconds to wait for the HTTP server to start.
26
+ exit_grace_period: Optional[int], # The time to wait for the HTTP server to exit gracefully.
27
+ ):
28
+ if not isinstance(port, int) or port < 1 or port > 65535:
29
+ raise InvalidError("Port must be a positive integer between 1 and 65535.")
30
+ if startup_timeout <= 0:
31
+ raise InvalidError("The `startup_timeout` argument must be positive.")
32
+ if exit_grace_period is not None and exit_grace_period < 0:
33
+ raise InvalidError("The `exit_grace_period` argument must be non-negative.")
34
+ if not proxy_regions:
35
+ raise InvalidError("The `proxy_regions` argument must be non-empty.")
36
+
37
+
38
+ class _Server:
39
+ """Server runs an HTTP server started in an `@modal.enter` method.
40
+
41
+ See [lifecycle hooks](https://modal.com/docs/guide/lifecycle-functions) for more information.
42
+
43
+ Generally, you will not construct a Server directly.
44
+ Instead, use the [`@app.server()`](https://modal.com/docs/reference/modal.App#server) decorator.
45
+
46
+ ```python notest
47
+ @app.server(port=8000, proxy_regions=["us-east", "us-west"])
48
+ class MyServer:
49
+ @modal.enter()
50
+ def start_server(self):
51
+ self.process = subprocess.Popen(["python3", "-m", "http.server", "8080"])
52
+ ```
53
+ """
54
+
55
+ _user_cls: Optional[type] = None # None if remote
56
+ _service_function: _Function
57
+ _app: Optional["modal.app._App"] = None # None if remote
58
+
59
+ def _get_user_cls(self) -> type:
60
+ assert self._user_cls is not None
61
+ return self._user_cls
62
+
63
+ def _get_app(self) -> "modal.app._App":
64
+ assert self._app
65
+ return self._app
66
+
67
+ def _get_service_function(self) -> _Function:
68
+ return self._service_function
69
+
70
+ @staticmethod
71
+ def _extract_user_cls(wrapped_user_cls: "type | _PartialFunction") -> type:
72
+ if isinstance(wrapped_user_cls, _PartialFunction):
73
+ assert wrapped_user_cls.user_cls
74
+ return wrapped_user_cls.user_cls
75
+ else:
76
+ return wrapped_user_cls
77
+
78
+ # ============ Live Methods ============
79
+
80
+ @live_method
81
+ async def get_urls(self) -> Optional[list[str]]:
82
+ return await self._get_service_function()._experimental_get_flash_urls()
83
+
84
+ @live_method
85
+ async def update_autoscaler(
86
+ self,
87
+ *,
88
+ min_containers: Optional[int] = None,
89
+ max_containers: Optional[int] = None,
90
+ buffer_containers: Optional[int] = None,
91
+ scaledown_window: Optional[int] = None,
92
+ ) -> None:
93
+ """Override the current autoscaler behavior for this Server.
94
+
95
+ Unspecified parameters will retain their current value.
96
+
97
+ Examples:
98
+ ```python notest
99
+ server = modal.Server.from_name("my-app", "Server")
100
+
101
+ # Always have at least 2 containers running, with an extra buffer of 2 containers
102
+ server.update_autoscaler(min_containers=2, buffer_containers=1)
103
+
104
+ # Limit this Server to avoid spinning up more than 5 containers
105
+ server.update_autoscaler(max_containers=5)
106
+ ```
107
+
108
+ """
109
+ return await self._get_service_function().update_autoscaler(
110
+ min_containers=min_containers,
111
+ max_containers=max_containers,
112
+ scaledown_window=scaledown_window,
113
+ buffer_containers=buffer_containers,
114
+ )
115
+
116
+ # ============ Hydration ============
117
+ async def hydrate(self, client: Optional[_Client] = None) -> "_Server":
118
+ # This is required since we want to support @livemethod() decorated methods
119
+ service_function = self._get_service_function()
120
+ await service_function.hydrate(client)
121
+ return self
122
+
123
+ # ============ Construction ============
124
+ @staticmethod
125
+ def _from_local(
126
+ wrapped_user_cls: "type | _PartialFunction",
127
+ app: "modal.app._App",
128
+ service_function: _Function,
129
+ ) -> "_Server":
130
+ """Create a Server from a local class definition."""
131
+
132
+ # Note: Validation should be done by the caller (app.server()) BEFORE creating the Server.
133
+ # Extract the underlying class if wrapped in a _PartialFunction (e.g., from @modal.clustered())
134
+ user_cls = _Server._extract_user_cls(wrapped_user_cls)
135
+
136
+ server = _Server()
137
+ server._app = app
138
+ server._user_cls = user_cls
139
+ server._service_function = service_function
140
+ return server
141
+
142
+ @classmethod
143
+ def from_name(
144
+ cls: type["_Server"],
145
+ app_name: str,
146
+ name: str,
147
+ *,
148
+ environment_name: Optional[str] = None,
149
+ client: Optional[_Client] = None,
150
+ ) -> "_Server":
151
+ """Reference a Server from a deployed App by its name.
152
+
153
+ This is a lazy method that defers hydrating the local
154
+ object with metadata from Modal servers until the first
155
+ time it is actually used.
156
+
157
+ ```python notest
158
+ server = modal.Server.from_name("other-app", "Server")
159
+ ```
160
+ """
161
+
162
+ load_context_overrides = LoadContext(client=client, environment_name=environment_name)
163
+
164
+ server = _Server()
165
+ server._service_function = _Function._from_name(
166
+ app_name,
167
+ name,
168
+ load_context_overrides=load_context_overrides,
169
+ )
170
+ return server
171
+
172
+ def _is_local(self) -> bool:
173
+ """Returns True if this Server has local source code available."""
174
+ return self._user_cls is not None
175
+
176
+ # ============ Validation ============
177
+
178
+ @staticmethod
179
+ def _validate_wrapped_user_cls_decorators(
180
+ wrapped_user_cls: "type | _PartialFunction", enable_memory_snapshot: bool
181
+ ):
182
+ user_cls = _Server._extract_user_cls(wrapped_user_cls)
183
+
184
+ if not inspect.isclass(user_cls):
185
+ raise TypeError("The @app.server() decorator must be used on a class.")
186
+
187
+ # Check for modal.parameter() - not allowed on server classes
188
+ params = {k: v for k, v in user_cls.__dict__.items() if is_parameter(v)}
189
+ if params:
190
+ raise InvalidError(
191
+ f"Server class {user_cls.__name__} cannot use modal.parameter(). "
192
+ "Servers do not support parameterization."
193
+ )
194
+
195
+ if not _find_partial_methods_for_user_cls(
196
+ user_cls, _PartialFunctionFlags.ENTER_PRE_SNAPSHOT
197
+ ) and not _find_partial_methods_for_user_cls(user_cls, _PartialFunctionFlags.ENTER_POST_SNAPSHOT):
198
+ raise InvalidError("Server class must have an @modal.enter() to setup the server.")
199
+
200
+ # Check for disallowed decorators
201
+ # @modal.method() not allowed
202
+ if _find_partial_methods_for_user_cls(user_cls, _PartialFunctionFlags.CALLABLE_INTERFACE).values():
203
+ raise InvalidError(
204
+ f"Server class {user_cls.__name__} cannot have `@modal.method()` decorated functions. "
205
+ "Servers only expose HTTP endpoints."
206
+ )
207
+ # @enter with snap=True without enable_memory_snapshot
208
+ if (
209
+ _find_partial_methods_for_user_cls(user_cls, _PartialFunctionFlags.ENTER_PRE_SNAPSHOT)
210
+ and not enable_memory_snapshot
211
+ ):
212
+ raise InvalidError(
213
+ "Server must have `enable_memory_snapshot=True` to use `snap=True` on `@modal.enter` methods."
214
+ )
215
+
216
+ if isinstance(wrapped_user_cls, _PartialFunction):
217
+ # @modal.concurrent not allowed on server classes
218
+ if wrapped_user_cls.flags & _PartialFunctionFlags.CONCURRENT:
219
+ raise InvalidError(
220
+ f"Server class {user_cls.__name__} cannot be decorated with `@modal.concurrent()`. "
221
+ "Please use `target_concurrency` param instead."
222
+ )
223
+ # @modal.http_server not allowed on server classes
224
+ if wrapped_user_cls.flags & _PartialFunctionFlags.HTTP_WEB_INTERFACE:
225
+ raise InvalidError(
226
+ f"Server class {user_cls.__name__} cannot have @modal.experimental.http_server() decorator. "
227
+ "Servers already expose HTTP endpoints."
228
+ )
229
+ # @modal.web_server not allowed on server classes
230
+ if wrapped_user_cls.flags & _PartialFunctionFlags.WEB_INTERFACE:
231
+ raise InvalidError(
232
+ f"Server class {user_cls.__name__} cannot be decorated with `@modal.web_server()`. "
233
+ "Servers already expose HTTP endpoints."
234
+ )
235
+
236
+ @staticmethod
237
+ def _validate_construction_mechanism(wrapped_user_cls: "type | _PartialFunction"):
238
+ """Validate that the server class doesn't have a custom constructor."""
239
+ # Extract the underlying class if wrapped in a _PartialFunction (e.g., from @modal.clustered())
240
+ user_cls = _Server._extract_user_cls(wrapped_user_cls)
241
+
242
+ if user_cls.__init__ != object.__init__: # type: ignore
243
+ raise InvalidError(
244
+ f"Server class {user_cls.__name__} cannot have a custom __init__ method. "
245
+ "Use @modal.enter() for initialization logic instead."
246
+ )
modal/client.pyi CHANGED
@@ -33,7 +33,7 @@ class _Client:
33
33
  server_url: str,
34
34
  client_type: int,
35
35
  credentials: typing.Optional[tuple[str, str]],
36
- version: str = "1.3.1.dev25",
36
+ version: str = "1.3.1.dev27",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  The Modal client object is not intended to be instantiated directly by users.
@@ -163,7 +163,7 @@ class Client:
163
163
  server_url: str,
164
164
  client_type: int,
165
165
  credentials: typing.Optional[tuple[str, str]],
166
- version: str = "1.3.1.dev25",
166
+ version: str = "1.3.1.dev27",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
@@ -15,6 +15,7 @@ from modal.cls import _Cls
15
15
  from modal.dict import _Dict
16
16
  from modal_proto import api_pb2
17
17
 
18
+ from .._server import validate_http_server_config
18
19
  from .._tunnel import _forward as _forward_tunnel
19
20
  from .._utils.async_utils import synchronize_api, synchronizer
20
21
  from ..client import _Client
@@ -142,7 +143,6 @@ class _FlashManager:
142
143
  )
143
144
  self.num_failures += 1
144
145
  await self.client.stub.FlashContainerDeregister(api_pb2.FlashContainerDeregisterRequest())
145
-
146
146
  except asyncio.CancelledError:
147
147
  logger.warning("[Modal Flash] Shutting down...")
148
148
  break
@@ -670,12 +670,7 @@ def _http_server(
670
670
  raise InvalidError(
671
671
  "Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.http_server()`."
672
672
  )
673
- if not isinstance(port, int) or port < 1 or port > 65535:
674
- raise InvalidError("First argument of `@http_server` must be a local port, such as `@http_server(8000)`.")
675
- if startup_timeout <= 0:
676
- raise InvalidError("The `startup_timeout` argument of `@http_server` must be positive.")
677
- if exit_grace_period is not None and exit_grace_period < 0:
678
- raise InvalidError("The `exit_grace_period` argument of `@http_server` must be non-negative.")
673
+ validate_http_server_config(port, proxy_regions, startup_timeout, exit_grace_period)
679
674
 
680
675
  from modal._partial_function import _PartialFunction, _PartialFunctionParams
681
676
 
modal/server.py ADDED
@@ -0,0 +1,5 @@
1
+ # Copyright Modal Labs 2026
2
+ from ._server import _Server
3
+ from ._utils.async_utils import synchronize_api
4
+
5
+ Server = synchronize_api(_Server, target_module=__name__)
modal/server.pyi ADDED
@@ -0,0 +1,147 @@
1
+ import modal.app
2
+ import modal.client
3
+ import modal.functions
4
+ import modal.partial_function
5
+ import typing
6
+ import typing_extensions
7
+
8
+ class Server:
9
+ """Server runs an HTTP server started in an `@modal.enter` method.
10
+
11
+ See [lifecycle hooks](https://modal.com/docs/guide/lifecycle-functions) for more information.
12
+
13
+ Generally, you will not construct a Server directly.
14
+ Instead, use the [`@app.server()`](https://modal.com/docs/reference/modal.App#server) decorator.
15
+
16
+ ```python notest
17
+ @app.server(port=8000, proxy_regions=["us-east", "us-west"])
18
+ class MyServer:
19
+ @modal.enter()
20
+ def start_server(self):
21
+ self.process = subprocess.Popen(["python3", "-m", "http.server", "8080"])
22
+ ```
23
+ """
24
+
25
+ _user_cls: typing.Optional[type]
26
+ _service_function: modal.functions.Function
27
+ _app: typing.Optional[modal.app.App]
28
+
29
+ def __init__(self, /, *args, **kwargs):
30
+ """Initialize self. See help(type(self)) for accurate signature."""
31
+ ...
32
+
33
+ def _get_user_cls(self) -> type: ...
34
+ def _get_app(self) -> modal.app.App: ...
35
+ def _get_service_function(self) -> modal.functions.Function: ...
36
+ @staticmethod
37
+ def _extract_user_cls(wrapped_user_cls: typing.Union[type, modal.partial_function.PartialFunction]) -> type: ...
38
+
39
+ class __get_urls_spec(typing_extensions.Protocol):
40
+ def __call__(self, /) -> typing.Optional[list[str]]: ...
41
+ async def aio(self, /) -> typing.Optional[list[str]]: ...
42
+
43
+ get_urls: __get_urls_spec
44
+
45
+ class __update_autoscaler_spec(typing_extensions.Protocol):
46
+ def __call__(
47
+ self,
48
+ /,
49
+ *,
50
+ min_containers: typing.Optional[int] = None,
51
+ max_containers: typing.Optional[int] = None,
52
+ buffer_containers: typing.Optional[int] = None,
53
+ scaledown_window: typing.Optional[int] = None,
54
+ ) -> None:
55
+ """Override the current autoscaler behavior for this Server.
56
+
57
+ Unspecified parameters will retain their current value.
58
+
59
+ Examples:
60
+ ```python notest
61
+ server = modal.Server.from_name("my-app", "Server")
62
+
63
+ # Always have at least 2 containers running, with an extra buffer of 2 containers
64
+ server.update_autoscaler(min_containers=2, buffer_containers=1)
65
+
66
+ # Limit this Server to avoid spinning up more than 5 containers
67
+ server.update_autoscaler(max_containers=5)
68
+ ```
69
+ """
70
+ ...
71
+
72
+ async def aio(
73
+ self,
74
+ /,
75
+ *,
76
+ min_containers: typing.Optional[int] = None,
77
+ max_containers: typing.Optional[int] = None,
78
+ buffer_containers: typing.Optional[int] = None,
79
+ scaledown_window: typing.Optional[int] = None,
80
+ ) -> None:
81
+ """Override the current autoscaler behavior for this Server.
82
+
83
+ Unspecified parameters will retain their current value.
84
+
85
+ Examples:
86
+ ```python notest
87
+ server = modal.Server.from_name("my-app", "Server")
88
+
89
+ # Always have at least 2 containers running, with an extra buffer of 2 containers
90
+ server.update_autoscaler(min_containers=2, buffer_containers=1)
91
+
92
+ # Limit this Server to avoid spinning up more than 5 containers
93
+ server.update_autoscaler(max_containers=5)
94
+ ```
95
+ """
96
+ ...
97
+
98
+ update_autoscaler: __update_autoscaler_spec
99
+
100
+ class __hydrate_spec(typing_extensions.Protocol):
101
+ def __call__(self, /, client: typing.Optional[modal.client.Client] = None) -> Server: ...
102
+ async def aio(self, /, client: typing.Optional[modal.client.Client] = None) -> Server: ...
103
+
104
+ hydrate: __hydrate_spec
105
+
106
+ @staticmethod
107
+ def _from_local(
108
+ wrapped_user_cls: typing.Union[type, modal.partial_function.PartialFunction],
109
+ app: modal.app.App,
110
+ service_function: modal.functions.Function,
111
+ ) -> Server:
112
+ """Create a Server from a local class definition."""
113
+ ...
114
+
115
+ @classmethod
116
+ def from_name(
117
+ cls: type[Server],
118
+ app_name: str,
119
+ name: str,
120
+ *,
121
+ environment_name: typing.Optional[str] = None,
122
+ client: typing.Optional[modal.client.Client] = None,
123
+ ) -> Server:
124
+ """Reference a Server from a deployed App by its name.
125
+
126
+ This is a lazy method that defers hydrating the local
127
+ object with metadata from Modal servers until the first
128
+ time it is actually used.
129
+
130
+ ```python notest
131
+ server = modal.Server.from_name("other-app", "Server")
132
+ ```
133
+ """
134
+ ...
135
+
136
+ def _is_local(self) -> bool:
137
+ """Returns True if this Server has local source code available."""
138
+ ...
139
+
140
+ @staticmethod
141
+ def _validate_wrapped_user_cls_decorators(
142
+ wrapped_user_cls: typing.Union[type, modal.partial_function.PartialFunction], enable_memory_snapshot: bool
143
+ ): ...
144
+ @staticmethod
145
+ def _validate_construction_mechanism(wrapped_user_cls: typing.Union[type, modal.partial_function.PartialFunction]):
146
+ """Validate that the server class doesn't have a custom constructor."""
147
+ ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.3.1.dev25
3
+ Version: 1.3.1.dev27
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -16,6 +16,7 @@ modal/_pty.py,sha256=E58MQ8d5-wkbMatRKpQR-G9FdbCRcZGiZxOpGy__VuY,1481
16
16
  modal/_resolver.py,sha256=01fdEP-ujXlw2LG0sthPqanm3b0DAiRG81xiQcHHkhY,7317
17
17
  modal/_resources.py,sha256=NMAp0GCLutiZI4GuKSIVnRHVlstoD3hNGUabjTUtzf4,1794
18
18
  modal/_serialization.py,sha256=Yc4czrt8x3xkX43oJngb7BSI4zPgtBVuifYrxTCdsAQ,26451
19
+ modal/_server.py,sha256=VTW3Yz5ANGV3P0LetFxUBXamlLO-59HkJZVBhzGvk-U,9626
19
20
  modal/_traceback.py,sha256=N0EumYnHHqPHkmP2L6cJKjTpoxc8QYmiofROmdgSthQ,6238
20
21
  modal/_tunnel.py,sha256=bbjVuWiDyC3m_NaVBpgoNQte-NM1FUtDVQsMobQbhc8,6578
21
22
  modal/_tunnel.pyi,sha256=FoedJzHymbPk4Bmnbszm6uhay2rMq1JS9mIiHELYf3k,13804
@@ -26,7 +27,7 @@ modal/app.pyi,sha256=0myfmGDqVoEoxTUWFG_Z4-0KFMlXZXe8k5hcFzl8k2I,50863
26
27
  modal/billing.py,sha256=zmQ3bcCJlwa4KD1IA_QgdWpm1pn13c-7qfy79iEauYI,195
27
28
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
28
29
  modal/client.py,sha256=CXnyJYLhY--xaCkjv3tpjYDca8AuXGQG1IiaXQVX4y4,14436
29
- modal/client.pyi,sha256=4BCHbzHfz3z6AxEpeRJ65DXjuk_Nb1khNKgdsjf7-_c,14473
30
+ modal/client.pyi,sha256=ZmumFl2q4ph1fS5pPEPk2SPq3kwPLmaoyIMsxqY1HqY,14473
30
31
  modal/cloud_bucket_mount.py,sha256=cvVj5Rt4nDHoQbTneoJPTaAcnl_Ab_mrsjnnzd0v9OI,6013
31
32
  modal/cloud_bucket_mount.pyi,sha256=vXusGMOUk9HjCs4lhxteuCK4xLQclPfugGWKo7LECEA,7539
32
33
  modal/cls.py,sha256=DNXcOrd9l2DoX-nLGdpwXm-N9V0BbQEvWA7d2UwFf44,41510
@@ -75,6 +76,8 @@ modal/schedule.py,sha256=ng0g0AqNY5GQI9KhkXZQ5Wam5G42glbkqVQsNpBtbDE,3078
75
76
  modal/scheduler_placement.py,sha256=BAREdOY5HzHpzSBqt6jDVR6YC_jYfHMVqOzkyqQfngU,1235
76
77
  modal/secret.py,sha256=XOHgwhjPJpzsIe7eo3DGAQ66hvyepc232UEEAREnjtk,18203
77
78
  modal/secret.pyi,sha256=wzGS_qOE0YwmoXvxWT5FyzKlTYS4wPFblbqDSuXRnoY,21016
79
+ modal/server.py,sha256=zWoDdtwCEKsKbFfWC4l7-NkpnJJybweC284VIoCavrI,164
80
+ modal/server.pyi,sha256=cE_OltARSf5qA6sqfveqlo8F64lLxURnagfZGfUNjcI,5204
78
81
  modal/serving.py,sha256=rfzj23Q4hPSMpc3vj_cQg3awkZULu4hLtRs83cOEB-w,4291
79
82
  modal/serving.pyi,sha256=eNqF_bhO_JQ0EDpqvSjSUdNs48vv7AcPXinp7GEs5wI,1966
80
83
  modal/snapshot.py,sha256=vIKxA1XRRsH8cFcpSUqC9M8NccKIaYaDpmiEC2clNks,2297
@@ -155,10 +158,10 @@ modal/cli/programs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxD
155
158
  modal/cli/programs/run_jupyter.py,sha256=IJw8nds8Cjl9j4dxBqMGxhz-bIyVX0kle7jbt8HFOhM,2688
156
159
  modal/cli/programs/vscode.py,sha256=E1aJPU7b8RWWj-JX71DifsCoLYsCAnre7lATiBJjUms,3386
157
160
  modal/experimental/__init__.py,sha256=GOuo3t4-Le0ZVddiHIOw0MQxkdTJge-bVbhDvgnblmQ,14573
158
- modal/experimental/flash.py,sha256=GJZwSr-4OHlP6gCIZdcbsSb8D8WX9xJwMphjwcfsRoU,31262
161
+ modal/experimental/flash.py,sha256=37gU4dX2kdGnbV9UXS2jLe8O-Z0Ed6p00xSobo9t9Bo,30930
159
162
  modal/experimental/flash.pyi,sha256=W62a0AhmLUuH9a0n8Y3-DYhyEEFH3-bRUQFphOQfKHg,16418
160
163
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
161
- modal-1.3.1.dev25.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
164
+ modal-1.3.1.dev27.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
162
165
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
163
166
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
164
167
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -166,10 +169,10 @@ modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,2
166
169
  modal_docs/mdmd/mdmd.py,sha256=tUTImNd4UMFk1opkaw8J672gX8AkBO5gbY2S_NMxsxs,7140
167
170
  modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
168
171
  modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
169
- modal_proto/api.proto,sha256=GXKOqBNz9URbPeIWchVUNfsQNxhNwD356aMfyX6d0uc,112224
172
+ modal_proto/api.proto,sha256=GhR7Tmb5fWMFV5yWGnRzQvyCO1_uW-u4YvCoI_H0Wco,112379
170
173
  modal_proto/api_grpc.py,sha256=ozNudkNLbPLp2bFm0bJtb0FHK9ACvJXXnGy771noCKc,135188
171
- modal_proto/api_pb2.py,sha256=DeyC243zTKxyoQR8koJFroYEdim1aK7gXYmcOKFNvi8,388892
172
- modal_proto/api_pb2.pyi,sha256=-a8hoKsyc2g_H_6SFD3ZRU_szPJ1cwTfkNrtO7I0vdM,549568
174
+ modal_proto/api_pb2.py,sha256=jDHJi7-YmJBj3J1NijOmxSJ7TbaMVovf9i-pHuRzw_M,388965
175
+ modal_proto/api_pb2.pyi,sha256=qyHiqRccVC9g9zk4VSHKpqZ9uJGj39V-YNTj83Pl1Ms,549964
173
176
  modal_proto/api_pb2_grpc.py,sha256=hHdfvfbS6HELZXTmHNfxlgPxFRDSc2B81G8YRtLwuHM,291347
174
177
  modal_proto/api_pb2_grpc.pyi,sha256=cqpXpAVrLoMQMCCyzyW5oXSFKmUPC9Y2wkTOpwNeAGM,68141
175
178
  modal_proto/modal_api_grpc.py,sha256=ls_o6oVYm8iup0nbKGPqs-CcJZJrry0eIY51mQFJRUc,21407
@@ -180,10 +183,10 @@ modal_proto/task_command_router_pb2.py,sha256=2Y8wRvnq4_7RiQ-5ZAPIgXaxrHv_MXzgS5
180
183
  modal_proto/task_command_router_pb2.pyi,sha256=yI4tFteErLlonFA2HyaWtONfhY_VGSe7KCWFIspvmkE,16323
181
184
  modal_proto/task_command_router_pb2_grpc.py,sha256=q2VWfK2-4uDcPaPLhAqPZkgxhuQRYnWmwUcnEbPbGuk,13895
182
185
  modal_proto/task_command_router_pb2_grpc.pyi,sha256=e1MM10VGUgDkm041v53QeTglGQPzTpA0FUsFtezjp74,4536
183
- modal_version/__init__.py,sha256=GdWEmyGQZpEknf8Gdx1td3885Y3nYJMNrxXHDq4e1zk,121
186
+ modal_version/__init__.py,sha256=VsHSyWD-hCduGdjxL8mpu77oL4fPvDjT-8RxQOikrtQ,121
184
187
  modal_version/__main__.py,sha256=nLUbubxKx7GXNzJIkTB7M7_u8080LyLM0IS0rzFkC_c,119
185
- modal-1.3.1.dev25.dist-info/METADATA,sha256=JxYK9z-i-7Ra1KUPC5DsrHrMGzqoxatUBTHHkVZ3Ans,2582
186
- modal-1.3.1.dev25.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
187
- modal-1.3.1.dev25.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
188
- modal-1.3.1.dev25.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
189
- modal-1.3.1.dev25.dist-info/RECORD,,
188
+ modal-1.3.1.dev27.dist-info/METADATA,sha256=mGy7XHf5rVm5pCNUQE9zEYgmBItQM9HyG8oFGJx36qQ,2582
189
+ modal-1.3.1.dev27.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
190
+ modal-1.3.1.dev27.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
191
+ modal-1.3.1.dev27.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
192
+ modal-1.3.1.dev27.dist-info/RECORD,,
modal_proto/api.proto CHANGED
@@ -1561,6 +1561,7 @@ message Function {
1561
1561
 
1562
1562
  bool single_use_containers = 91; // When True, containers will shut down after handling a single input
1563
1563
 
1564
+ bool is_server = 92; // When True, the function is a server-side function.
1564
1565
  }
1565
1566
 
1566
1567
  message FunctionAsyncInvokeRequest {
@@ -1744,6 +1745,8 @@ message FunctionData {
1744
1745
  // Attribute on the module with the implementation, which may differ from function_name
1745
1746
  // when the user provided a custom name= for the Function inside the Application namespace
1746
1747
  string implementation_name = 40;
1748
+
1749
+ bool is_server = 41; // When True, the function is a server-side function.
1747
1750
  }
1748
1751
 
1749
1752
  message FunctionExtended {