# Copyright 2024 Flower Labs GmbH. All Rights Reserved.## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.# =============================================================================="""Flower ServerApp."""fromtypingimportCallable,Optionalfromflwr.commonimportContextfromflwr.common.loggerimport(warn_deprecated_feature_with_example,warn_preview_feature,)fromflwr.server.strategyimportStrategyfrom.client_managerimportClientManagerfrom.compatimportstart_driverfrom.driverimportDriverfrom.serverimportServerfrom.server_configimportServerConfigfrom.typingimportServerAppCallable,ServerFnSERVER_FN_USAGE_EXAMPLE=""" def server_fn(context: Context): server_config = ServerConfig(num_rounds=3) strategy = FedAvg() return ServerAppComponents( strategy=strategy, server_config=server_config, ) app = ServerApp(server_fn=server_fn)"""
[docs]classServerApp:"""Flower ServerApp. Examples -------- Use the `ServerApp` with an existing `Strategy`: >>> def server_fn(context: Context): >>> server_config = ServerConfig(num_rounds=3) >>> strategy = FedAvg() >>> return ServerAppComponents( >>> strategy=strategy, >>> server_config=server_config, >>> ) >>> >>> app = ServerApp(server_fn=server_fn) Use the `ServerApp` with a custom main function: >>> app = ServerApp() >>> >>> @app.main() >>> def main(driver: Driver, context: Context) -> None: >>> print("ServerApp running") """# pylint: disable=too-many-arguments,too-many-positional-argumentsdef__init__(self,server:Optional[Server]=None,config:Optional[ServerConfig]=None,strategy:Optional[Strategy]=None,client_manager:Optional[ClientManager]=None,server_fn:Optional[ServerFn]=None,)->None:ifany([server,config,strategy,client_manager]):warn_deprecated_feature_with_example(deprecation_message="Passing either `server`, `config`, `strategy` or ""`client_manager` directly to the ServerApp ""constructor is deprecated.",example_message="Pass `ServerApp` arguments wrapped ""in a `flwr.server.ServerAppComponents` object that gets ""returned by a function passed as the `server_fn` argument ""to the `ServerApp` constructor. For example: ",code_example=SERVER_FN_USAGE_EXAMPLE,)ifserver_fn:raiseValueError("Passing `server_fn` is incompatible with passing the ""other arguments (now deprecated) to ServerApp. ""Use `server_fn` exclusively.")self._server=serverself._config=configself._strategy=strategyself._client_manager=client_managerself._server_fn=server_fnself._main:Optional[ServerAppCallable]=Nonedef__call__(self,driver:Driver,context:Context)->None:"""Execute `ServerApp`."""# Compatibility modeifnotself._main:ifself._server_fn:# Execute server_fn()components=self._server_fn(context)self._server=components.serverself._config=components.configself._strategy=components.strategyself._client_manager=components.client_managerstart_driver(server=self._server,config=self._config,strategy=self._strategy,client_manager=self._client_manager,driver=driver,)return# New execution modeself._main(driver,context)
[docs]defmain(self)->Callable[[ServerAppCallable],ServerAppCallable]:"""Return a decorator that registers the main fn with the server app. Examples -------- >>> app = ServerApp() >>> >>> @app.main() >>> def main(driver: Driver, context: Context) -> None: >>> print("ServerApp running") """defmain_decorator(main_fn:ServerAppCallable)->ServerAppCallable:"""Register the main fn with the ServerApp object."""ifself._serverorself._configorself._strategyorself._client_manager:raiseValueError("""Use either a custom main function or a `Strategy`, but not both. Use the `ServerApp` with an existing `Strategy`: >>> server_config = ServerConfig(num_rounds=3) >>> strategy = FedAvg() >>> >>> app = ServerApp( >>> server_config=server_config, >>> strategy=strategy, >>> ) Use the `ServerApp` with a custom main function: >>> app = ServerApp() >>> >>> @app.main() >>> def main(driver: Driver, context: Context) -> None: >>> print("ServerApp running") """,)warn_preview_feature("ServerApp-register-main-function")# Register provided function with the ServerApp objectself._main=main_fn# Return provided function unmodifiedreturnmain_fnreturnmain_decorator
classLoadServerAppError(Exception):"""Error when trying to load `ServerApp`."""