Skip to content

Commit 51fbf7b

Browse files
committed
rename to user cls
1 parent 323ea20 commit 51fbf7b

2 files changed

Lines changed: 66 additions & 75 deletions

File tree

modal/app.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,22 +1280,22 @@ def stop(self):
12801280
if env:
12811281
secrets_list.append(_Secret.from_dict(env))
12821282

1283-
def wrapper(wrapped_server: _Server) -> _Server:
1284-
_Server.validate_wrapped_server_decorators(wrapped_server, enable_memory_snapshot)
1283+
def wrapper(wrapped_user_cls: _Server) -> _Server:
1284+
_Server.validate_wrapped_user_cls_decorators(wrapped_user_cls, enable_memory_snapshot)
12851285

12861286
cluster_size = None
12871287
rdma = None
1288-
if isinstance(wrapped_server, _PartialFunction):
1289-
if wrapped_server.flags & _PartialFunctionFlags.CLUSTERED:
1290-
cluster_size = wrapped_server.params.cluster_size
1291-
rdma = wrapped_server.params.rdma
1288+
if isinstance(wrapped_user_cls, _PartialFunction):
1289+
if wrapped_user_cls.flags & _PartialFunctionFlags.CLUSTERED:
1290+
cluster_size = wrapped_user_cls.params.cluster_size
1291+
rdma = wrapped_user_cls.params.rdma
12921292

12931293
local_state = self._local_state
12941294

12951295
# Validate the server class
1296-
_Server.validate_construction_mechanism(wrapped_server)
1296+
_Server.validate_construction_mechanism(wrapped_user_cls)
12971297
# Create the FunctionInfo for the server, note we treat FunctionInfo as a class for servers
1298-
info = FunctionInfo(None, serialized=serialized, user_cls=wrapped_server)
1298+
info = FunctionInfo(None, serialized=serialized, user_cls=wrapped_user_cls)
12991299
# Create the service function
13001300
service_function = _Function.from_local(
13011301
info,
@@ -1336,15 +1336,15 @@ def wrapper(wrapped_server: _Server) -> _Server:
13361336
self._add_function(service_function, is_web_endpoint=False)
13371337

13381338
# Create the Server object
1339-
server: _Server = _Server.from_local(wrapped_server, self, service_function)
1339+
server: _Server = _Server.from_local(wrapped_user_cls, self, service_function)
13401340

13411341
# Mark lifecycle methods as registered
13421342
for flag in (~_PartialFunctionFlags.interface_flags(),):
1343-
for partial in _find_partial_methods_for_user_cls(wrapped_server, flag).values():
1343+
for partial in _find_partial_methods_for_user_cls(wrapped_user_cls, flag).values():
13441344
partial.registered = True
13451345

13461346
# Register the server with the app
1347-
tag: str = wrapped_server.__name__
1347+
tag: str = wrapped_user_cls.__name__
13481348
self._add_class(tag, server)
13491349

13501350
return server # type: ignore

modal/server.py

Lines changed: 55 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Copyright Modal Labs 2025
22
import inspect
33
import typing
4-
from collections.abc import Collection
54
from typing import Any, Optional
65

76
from google.protobuf.message import Message
@@ -52,43 +51,44 @@ def stop(self):
5251
```
5352
"""
5453

55-
# Backend returns "cs-" IDs for servers (since it is defined on classes).
56-
# Set directly to avoid overwriting _Cls's registration in _Object._prefix_to_type.
54+
# Maps 1-1 w function
5755
_type_prefix = "cs"
5856

5957
_app: Optional["modal.app._App"] = None
6058
_name: Optional[str] = None
61-
_user_server: Optional[type] = None
62-
_user_server_function: Optional[_Function] = None
63-
# Runtime state for lifecycle management
64-
_user_server_instance: Optional[Any] = None
59+
# Raw user defined class
60+
_user_cls: Optional[type] = None
61+
# Instantiated raw user class
62+
_user_cls_instance: Optional[Any] = None
63+
# Function interface with server backend
64+
_service_function: Optional[_Function] = None
6565
_has_entered: bool = False
6666

6767
def _initialize_from_empty(self):
68-
self._user_server = None
69-
self._user_server_function = None
68+
self._user_cls = None
7069
self._name = None
7170
self._app = None
72-
self._user_server_instance = None
71+
self._user_cls_instance = None
72+
self._service_function = None
7373
self._has_entered = False
7474

7575
def _initialize_from_other(self, other: "_Server"):
7676
super()._initialize_from_other(other)
7777
self._app = other._app
78-
self._user_server = other._user_server
79-
self._user_server_function = other._user_server_function
78+
self._user_cls = other._user_cls
79+
self._user_cls_instance = other._user_cls_instance
80+
self._service_function = other._service_function
8081
self._name = other._name
8182
self._load_context_overrides = other._load_context_overrides
82-
self._user_server_instance = other._user_server_instance
8383
self._has_entered = other._has_entered
8484

8585
def _get_app(self) -> "modal.app._App":
8686
assert self._app is not None
8787
return self._app
8888

89-
def _get_user_server(self) -> type:
90-
assert self._user_server is not None
91-
return self._user_server
89+
def _get_user_cls(self) -> type:
90+
assert self._user_cls is not None
91+
return self._user_cls
9292

9393
def _get_name(self) -> str:
9494
assert self._name is not None
@@ -99,33 +99,29 @@ def __name__(self) -> str:
9999
"""Return the name of the server class for compatibility with code expecting class-like objects."""
100100
return self._name or ""
101101

102-
def _get_method_names(self) -> Collection[str]:
103-
"""Servers don't have methods - return empty collection for compatibility."""
104-
return []
105-
106102
def _get_service_function(self) -> _Function:
107-
assert self._user_server_function is not None
108-
return self._user_server_function
103+
assert self._service_function is not None
104+
return self._service_function
109105

110106
# ============ Lifecycle Management ============
111107

112-
def _get_or_create_instance(self) -> Any:
108+
def _get_or_create_user_cls_instance(self) -> Any:
113109
"""Get or construct the local server instance."""
114-
if self._user_server_instance is None:
115-
assert self._user_server is not None
116-
self._user_server_instance = object.__new__(self._user_server)
117-
return self._user_server_instance
110+
if self._user_cls_instance is None:
111+
assert self._user_cls is not None
112+
self._user_cls_instance = object.__new__(self._user_cls)
113+
return self._user_cls_instance
118114

119115
def _enter(self):
120116
"""Run @enter lifecycle hooks (sync version)."""
121-
assert self._user_server is not None
117+
assert self._user_cls is not None
122118
if self._has_entered:
123119
return
124120

125-
user_server_instance = self._get_or_create_instance()
121+
user_cls_instance = self._get_or_create_user_cls_instance()
126122

127123
# Support __enter__ context manager protocol
128-
enter_method = getattr(user_server_instance, "__enter__", None)
124+
enter_method = getattr(user_cls_instance, "__enter__", None)
129125
if enter_method is not None:
130126
enter_method()
131127

@@ -134,7 +130,7 @@ def _enter(self):
134130
_PartialFunctionFlags.ENTER_PRE_SNAPSHOT,
135131
_PartialFunctionFlags.ENTER_POST_SNAPSHOT,
136132
):
137-
for enter_method in _find_callables_for_obj(user_server_instance, method_flag).values():
133+
for enter_method in _find_callables_for_obj(user_cls_instance, method_flag).values():
138134
enter_method()
139135

140136
self._has_entered = True
@@ -145,10 +141,10 @@ async def _aenter(self):
145141
if self._has_entered:
146142
return
147143

148-
user_server_instance = self._get_or_create_instance()
144+
user_cls_instance = self._get_or_create_user_cls_instance()
149145

150-
aenter_method = getattr(user_server_instance, "__aenter__", None)
151-
enter_method = getattr(user_server_instance, "__enter__", None)
146+
aenter_method = getattr(user_cls_instance, "__aenter__", None)
147+
enter_method = getattr(user_cls_instance, "__enter__", None)
152148
if aenter_method is not None:
153149
await aenter_method()
154150
elif enter_method is not None:
@@ -159,7 +155,7 @@ async def _aenter(self):
159155
_PartialFunctionFlags.ENTER_PRE_SNAPSHOT,
160156
_PartialFunctionFlags.ENTER_POST_SNAPSHOT,
161157
):
162-
for enter_method in _find_callables_for_obj(user_server_instance, method_flag).values():
158+
for enter_method in _find_callables_for_obj(user_cls_instance, method_flag).values():
163159
enter_method()
164160

165161
self._has_entered = True
@@ -196,8 +192,6 @@ async def update_autoscaler(
196192
TODO(claudia): Add examples
197193
198194
"""
199-
# if target_concurrency is not None:
200-
# await self._get_service_function().update_autoscaler(target_concurrency=target_concurrency)
201195
return await self._get_service_function().update_autoscaler(
202196
min_containers=min_containers,
203197
max_containers=max_containers,
@@ -208,75 +202,72 @@ async def update_autoscaler(
208202
# ============ Hydration ============
209203

210204
def _hydrate_metadata(self, metadata: Optional[Message]):
211-
# Servers don't have method metadata like Cls does
212-
if metadata is not None:
213-
assert isinstance(metadata, api_pb2.ClassHandleMetadata)
214-
# Just verify the service function is hydrated
215205
service_function = self._get_service_function()
216206
assert service_function.is_hydrated
217207

218208
# ============ Construction ============
219209

220210
@staticmethod
221-
def validate_wrapped_server_decorators(wrapped_server: type, enable_memory_snapshot: bool):
222-
if not inspect.isclass(wrapped_server):
211+
def validate_wrapped_user_cls_decorators(wrapped_user_cls: type, enable_memory_snapshot: bool):
212+
# TODO(claudia): Add tests for this, ensure that parametrization is not allowed.
213+
if not inspect.isclass(wrapped_user_cls):
223214
raise TypeError("The @app.server() decorator must be used on a class.")
224215
if not _find_partial_methods_for_user_cls(
225-
wrapped_server, _PartialFunctionFlags.ENTER_PRE_SNAPSHOT
226-
) and not _find_partial_methods_for_user_cls(wrapped_server, _PartialFunctionFlags.ENTER_POST_SNAPSHOT):
216+
wrapped_user_cls, _PartialFunctionFlags.ENTER_PRE_SNAPSHOT
217+
) and not _find_partial_methods_for_user_cls(wrapped_user_cls, _PartialFunctionFlags.ENTER_POST_SNAPSHOT):
227218
raise InvalidError("Server class must have an @modal.enter() to setup the server.")
228219

229220
# Check for disallowed decorators
230221
# @modal.method() not allowed
231-
if _find_partial_methods_for_user_cls(wrapped_server, _PartialFunctionFlags.CALLABLE_INTERFACE).values():
222+
if _find_partial_methods_for_user_cls(wrapped_user_cls, _PartialFunctionFlags.CALLABLE_INTERFACE).values():
232223
raise InvalidError(
233-
f"Server class {wrapped_server.__name__} cannot have @method() decorated functions. "
224+
f"Server class {wrapped_user_cls.__name__} cannot have @method() decorated functions. "
234225
"Servers only expose HTTP endpoints."
235226
)
236227
# @enter with snap=True without enable_memory_snapshot
237228
if (
238-
_find_partial_methods_for_user_cls(wrapped_server, _PartialFunctionFlags.ENTER_PRE_SNAPSHOT)
229+
_find_partial_methods_for_user_cls(wrapped_user_cls, _PartialFunctionFlags.ENTER_PRE_SNAPSHOT)
239230
and not enable_memory_snapshot
240231
):
241232
raise InvalidError("Server must have `enable_memory_snapshot=True` to use `snap=True` on @enter methods.")
242233

243-
if isinstance(wrapped_server, _PartialFunction):
234+
if isinstance(wrapped_user_cls, _PartialFunction):
244235
# @modal.concurrent not allowed on server classes
245-
if wrapped_server.flags & _PartialFunctionFlags.CONCURRENT:
236+
if wrapped_user_cls.flags & _PartialFunctionFlags.CONCURRENT:
246237
raise InvalidError(
247-
f"Server class {wrapped_server.__name__} cannot have @concurrent() decorated functions. "
238+
f"Server class {wrapped_user_cls.__name__} cannot have @concurrent() decorated functions. "
248239
"Please use `target_concurrency` param instead."
249240
)
250241
# @modal.http_server not allowed on server classes
251-
if wrapped_server.flags & _PartialFunctionFlags.HTTP_WEB_INTERFACE:
242+
if wrapped_user_cls.flags & _PartialFunctionFlags.HTTP_WEB_INTERFACE:
252243
raise InvalidError(
253-
f"Server class {wrapped_server.__name__} cannot have @modal.experimental.http_server() decorator. "
244+
f"Server class {wrapped_user_cls.__name__} cannot have @modal.http_server() decorator. "
254245
"Servers already expose HTTP endpoints."
255246
)
256247

257248
@staticmethod
258-
def validate_construction_mechanism(user_server: type):
249+
def validate_construction_mechanism(user_cls: type):
259250
"""Validate that the server class doesn't have a custom constructor."""
260-
if user_server.__init__ != object.__init__:
251+
if user_cls.__init__ != object.__init__:
261252
raise InvalidError(
262-
f"Server class {user_server.__name__} cannot have a custom __init__ method. "
253+
f"Server class {user_cls.__name__} cannot have a custom __init__ method. "
263254
"Use @modal.enter() for initialization logic instead."
264255
)
265256

266257
@staticmethod
267258
def from_local(
268-
user_server: type,
259+
user_cls: type,
269260
app: "modal.app._App",
270261
service_function: _Function,
271262
) -> "_Server":
272263
"""Create a Server from a local class definition."""
273-
_Server.validate_wrapped_server_decorators(user_server, enable_memory_snapshot=False)
264+
_Server.validate_wrapped_user_cls_decorators(user_cls, enable_memory_snapshot=False)
274265
# Validate - no custom constructors allowed
275-
_Server.validate_construction_mechanism(user_server)
266+
_Server.validate_construction_mechanism(user_cls)
276267

277268
# Mark lifecycle methods as registered to avoid warnings
278269
lifecycle_flags = ~_PartialFunctionFlags.interface_flags()
279-
lifecycle_partials = _find_partial_methods_for_user_cls(user_server, lifecycle_flags)
270+
lifecycle_partials = _find_partial_methods_for_user_cls(user_cls, lifecycle_flags)
280271
for partial_function in lifecycle_partials.values():
281272
partial_function.registered = True
282273

@@ -298,17 +289,17 @@ async def _load(
298289
resp = await load_context.client.stub.ClassCreate(req)
299290
self._hydrate(resp.class_id, load_context.client, resp.handle_metadata)
300291

301-
rep = f"Server({user_server.__name__})"
292+
rep = f"Server({user_cls.__name__})"
302293
server: _Server = _Server._from_loader(
303294
_load,
304295
rep,
305296
deps=_deps,
306297
load_context_overrides=app._root_load_context,
307298
)
308299
server._app = app
309-
server._user_server = user_server
310-
server._user_server_function = service_function
311-
server._name = user_server.__name__
300+
server._user_cls = user_cls
301+
server._service_function = service_function
302+
server._name = user_cls.__name__
312303
return server
313304

314305
@classmethod

0 commit comments

Comments
 (0)