Skip to content

Commit 190df50

Browse files
committed
Removal of a bunch of legacy class stuff from server
1 parent 6710686 commit 190df50

2 files changed

Lines changed: 14 additions & 128 deletions

File tree

modal/_runtime/user_code_imports.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,8 @@ def import_class_service(
558558
# Servers have no methods, just lifecycle hooks
559559
method_partials = {}
560560
# Get or create instance of the user's server class
561-
user_cls_instance = _server._get_or_create_user_cls_instance()
561+
user_cls = _server._get_user_cls()
562+
user_cls_instance = user_cls()
562563
else:
563564
# Undecorated user class (serialized or local scope-decoration).
564565
service_deps = None # we can't infer service deps for now

modal/server.py

Lines changed: 12 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
# Copyright Modal Labs 2025
22
import inspect
33
import typing
4-
from typing import Any, Optional
5-
6-
from google.protobuf.message import Message
4+
from typing import Optional
75

86
from ._functions import _Function
97
from ._load_context import LoadContext
108
from ._object import live_method
119
from ._partial_function import (
12-
_find_callables_for_obj,
1310
_find_partial_methods_for_user_cls,
1411
_PartialFunction,
1512
_PartialFunctionFlags,
1613
)
17-
from ._utils.async_utils import synchronize_api, synchronizer
18-
from ._utils.deprecation import warn_if_passing_namespace
14+
from ._utils.async_utils import synchronize_api
1915
from .client import _Client
2016
from .cls import is_parameter
2117
from .exception import InvalidError
@@ -35,134 +31,29 @@ class _Server:
3531
TODO(claudia): Add examples
3632
"""
3733

38-
# Maps 1-1 w function
39-
_type_prefix = "fu"
40-
41-
_app: Optional["modal.app._App"] = None
42-
_name: Optional[str] = None
43-
# Raw user defined class
44-
_user_cls: Optional[type] = None
45-
# Instantiated raw user class
46-
_user_cls_instance: Optional[Any] = None
47-
# Function interface with server backend
48-
_service_function: Optional[_Function] = None
49-
_has_entered: bool = False
50-
51-
def __init__(self):
52-
self._initialize_from_empty()
53-
54-
def _initialize_from_empty(self):
55-
self._app = None
56-
self._name = None
57-
self._user_cls = None
58-
self._user_cls_instance = None
59-
self._service_function = None
60-
self._has_entered = False
61-
62-
def _initialize_from_other(self, other: "_Server"):
63-
self._app = other._app
64-
self._name = other._name
65-
self._user_cls = other._user_cls
66-
self._user_cls_instance = other._user_cls_instance
67-
self._service_function = other._service_function
68-
self._has_entered = other._has_entered
69-
70-
def _get_user_cls(self) -> type:
71-
assert self._user_cls is not None
72-
return self._user_cls
73-
74-
def _get_name(self) -> str:
75-
assert self._name is not None
76-
return self._name
34+
_user_cls: Optional[type] = None # None if remote
35+
_service_function: _Function
36+
_app: Optional["modal.app._App"] = None # None if remote
7737

7838
def _get_app(self) -> "modal.app._App":
79-
assert self._app is not None
39+
assert self._app, "app can only be extracted for local Server (in container entrypoint)"
8040
return self._app
8141

82-
@property
83-
def __name__(self) -> str:
84-
"""Return the name of the server class for compatibility with code expecting class-like objects."""
85-
return self._name or ""
42+
def _get_user_cls(self) -> type:
43+
assert self._user_cls is not None
44+
return self._user_cls
8645

8746
def _get_service_function(self) -> _Function:
88-
assert self._service_function is not None
8947
return self._service_function
9048

9149
@staticmethod
9250
def _extract_user_cls(wrapped_user_cls: "type | _PartialFunction") -> type:
9351
if isinstance(wrapped_user_cls, _PartialFunction):
52+
assert wrapped_user_cls.user_cls, "Non-class partial used as app.server input"
9453
return wrapped_user_cls.user_cls
9554
else:
9655
return wrapped_user_cls
9756

98-
# ============ Lifecycle Management ============
99-
100-
def _get_or_create_user_cls_instance(self) -> Any:
101-
"""Get or construct the local server instance."""
102-
if self._user_cls_instance is None:
103-
assert self._user_cls is not None
104-
self._user_cls_instance = object.__new__(self._user_cls)
105-
return self._user_cls_instance
106-
107-
def _enter(self):
108-
"""Run @enter lifecycle hooks (sync version)."""
109-
assert self._user_cls is not None
110-
if self._has_entered:
111-
return
112-
113-
user_cls_instance = self._get_or_create_user_cls_instance()
114-
115-
# Support __enter__ context manager protocol
116-
enter_method = getattr(user_cls_instance, "__enter__", None)
117-
if enter_method is not None:
118-
enter_method()
119-
120-
# Run @modal.enter() decorated methods
121-
for method_flag in (
122-
_PartialFunctionFlags.ENTER_PRE_SNAPSHOT,
123-
_PartialFunctionFlags.ENTER_POST_SNAPSHOT,
124-
):
125-
for enter_method in _find_callables_for_obj(user_cls_instance, method_flag).values():
126-
enter_method()
127-
128-
self._has_entered = True
129-
130-
@synchronizer.nowrap
131-
async def _aenter(self):
132-
"""Run @enter lifecycle hooks (async version)."""
133-
assert self._user_cls is not None
134-
if self._has_entered:
135-
return
136-
137-
user_cls_instance = self._get_or_create_user_cls_instance()
138-
139-
aenter_method = getattr(user_cls_instance, "__aenter__", None)
140-
enter_method = getattr(user_cls_instance, "__enter__", None)
141-
if aenter_method is not None:
142-
await aenter_method()
143-
elif enter_method is not None:
144-
enter_method()
145-
146-
# Run @modal.enter() decorated methods
147-
for method_flag in (
148-
_PartialFunctionFlags.ENTER_PRE_SNAPSHOT,
149-
_PartialFunctionFlags.ENTER_POST_SNAPSHOT,
150-
):
151-
for enter_method in _find_callables_for_obj(user_cls_instance, method_flag).values():
152-
res = enter_method()
153-
if inspect.iscoroutine(res):
154-
await res
155-
156-
self._has_entered = True
157-
158-
@property
159-
def _entered(self) -> bool:
160-
return self._has_entered
161-
162-
@_entered.setter
163-
def _entered(self, val: bool):
164-
self._has_entered = val
165-
16657
# ============ Live Methods ============
16758

16859
@live_method
@@ -196,10 +87,6 @@ async def update_autoscaler(
19687

19788
# ============ Hydration ============
19889

199-
def _hydrate_metadata(self, metadata: Optional[Message]):
200-
service_function = self._get_service_function()
201-
assert service_function.is_hydrated
202-
20390
async def hydrate(self, client: Optional[_Client] = None) -> "_Server":
20491
"""Hydrate the server by hydrating its underlying service function."""
20592
# This is required since we want to support @livemethod() decorated methods
@@ -294,10 +181,10 @@ def from_local(
294181
partial_function.registered = True
295182

296183
server = _Server()
297-
server._app = app
298184
server._user_cls = user_cls
185+
server._app = app
186+
299187
server._service_function = service_function
300-
server._name = user_cls.__name__
301188
return server
302189

303190
@classmethod
@@ -306,7 +193,6 @@ def from_name(
306193
app_name: str,
307194
name: str,
308195
*,
309-
namespace: Any = None, # Deprecated, hidden
310196
environment_name: Optional[str] = None,
311197
client: Optional[_Client] = None,
312198
) -> "_Server":
@@ -318,7 +204,6 @@ def from_name(
318204
319205
TODO(claudia): Add examples
320206
"""
321-
warn_if_passing_namespace(namespace, "modal.Server.from_name")
322207

323208
load_context_overrides = LoadContext(client=client, environment_name=environment_name)
324209

0 commit comments

Comments
 (0)