@@ -39,11 +39,11 @@ def _get_user_cls(self) -> type:
3939 return self ._user_cls
4040
4141 def _get_app (self ) -> "modal.app._App" :
42- assert self ._app , "app can only be extracted for local Server (in container entrypoint)"
42+ assert self ._app , "App can only be extracted for a local Server (in container entrypoint)"
4343 return self ._app
4444
4545 def _get_service_function (self ) -> _Function :
46- assert self ._service_function is not None # don't we need this?
46+ assert self ._service_function is not None
4747 return self ._service_function
4848
4949 @staticmethod
@@ -86,15 +86,63 @@ async def update_autoscaler(
8686
8787 # ============ Hydration ============
8888 async def hydrate (self , client : Optional [_Client ] = None ) -> "_Server" :
89- """Hydrate the server by hydrating its underlying service function."""
9089 # This is required since we want to support @livemethod() decorated methods
91- # and is normally handled by the _Object.hydrate() method
92- # but we only want to hydrate the service function.
9390 service_function = self ._get_service_function ()
9491 await service_function .hydrate (client )
9592 return self
9693
9794 # ============ Construction ============
95+ @staticmethod
96+ def from_local (
97+ wrapped_user_cls : "type | _PartialFunction" ,
98+ app : "modal.app._App" ,
99+ service_function : _Function ,
100+ ) -> "_Server" :
101+ """Create a Server from a local class definition."""
102+
103+ # Note: Validation should be done by the caller (app.server()) BEFORE creating the Server.
104+ # Extract the underlying class if wrapped in a _PartialFunction (e.g., from @modal.clustered())
105+ user_cls = _Server ._extract_user_cls (wrapped_user_cls )
106+
107+ server = _Server ()
108+ server ._app = app
109+ server ._user_cls = user_cls
110+ server ._service_function = service_function
111+ return server
112+
113+ @classmethod
114+ def from_name (
115+ cls : type ["_Server" ],
116+ app_name : str ,
117+ name : str ,
118+ * ,
119+ environment_name : Optional [str ] = None ,
120+ client : Optional [_Client ] = None ,
121+ ) -> "_Server" :
122+ """Reference a Server from a deployed App by its name.
123+
124+ This is a lazy method that defers hydrating the local
125+ object with metadata from Modal servers until the first
126+ time it is actually used.
127+
128+ TODO(claudia): Add examples
129+ """
130+
131+ load_context_overrides = LoadContext (client = client , environment_name = environment_name )
132+
133+ server = _Server ()
134+ server ._service_function = _Function ._from_name (
135+ app_name ,
136+ name ,
137+ load_context_overrides = load_context_overrides ,
138+ )
139+ return server
140+
141+ def _is_local (self ) -> bool :
142+ """Returns True if this Server has local source code available."""
143+ return self ._user_cls is not None
144+
145+ # ============ Validation ============
98146
99147 @staticmethod
100148 def _validate_wrapped_user_cls_decorators (
@@ -157,64 +205,3 @@ def validate_construction_mechanism(wrapped_user_cls: "type | _PartialFunction")
157205 f"Server class { user_cls .__name__ } cannot have a custom __init__ method. "
158206 "Use @modal.enter() for initialization logic instead."
159207 )
160-
161- @staticmethod
162- def from_local (
163- wrapped_user_cls : "type | _PartialFunction" ,
164- app : "modal.app._App" ,
165- service_function : _Function ,
166- ) -> "_Server" :
167- """Create a Server from a local class definition.
168-
169- Note: Validation should be done by the caller (app.server()) BEFORE creating
170- the service function, so we don't repeat it here.
171- """
172- # Extract the underlying class if wrapped in a _PartialFunction (e.g., from @modal.clustered())
173- user_cls = _Server ._extract_user_cls (wrapped_user_cls )
174-
175- # Mark lifecycle methods as registered to avoid warnings
176- lifecycle_flags = ~ _PartialFunctionFlags .interface_flags ()
177- lifecycle_partials = _find_partial_methods_for_user_cls (user_cls , lifecycle_flags )
178- for partial_function in lifecycle_partials .values ():
179- partial_function .registered = True
180-
181- server = _Server ()
182- server ._app = app
183- server ._user_cls = user_cls
184- server ._service_function = service_function
185- return server
186-
187- @classmethod
188- def from_name (
189- cls : type ["_Server" ],
190- app_name : str ,
191- name : str ,
192- * ,
193- environment_name : Optional [str ] = None ,
194- client : Optional [_Client ] = None ,
195- ) -> "_Server" :
196- """Reference a Server from a deployed App by its name.
197-
198- This is a lazy method that defers hydrating the local
199- object with metadata from Modal servers until the first
200- time it is actually used.
201-
202- TODO(claudia): Add examples
203- """
204-
205- load_context_overrides = LoadContext (client = client , environment_name = environment_name )
206-
207- # Server service functions use "#ClassName" naming convention
208- service_function_name = f"#{ name } "
209-
210- server = _Server ()
211- server ._service_function = _Function ._from_name (
212- app_name ,
213- service_function_name ,
214- load_context_overrides = load_context_overrides ,
215- )
216- return server
217-
218- def _is_local (self ) -> bool :
219- """Returns True if this Server has local source code available."""
220- return self ._user_cls is not None
0 commit comments