flwr-nightly 1.8.0.dev20240315__py3-none-any.whl → 1.15.0.dev20250115__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (312) hide show
  1. flwr/cli/app.py +16 -2
  2. flwr/cli/build.py +181 -0
  3. flwr/cli/cli_user_auth_interceptor.py +90 -0
  4. flwr/cli/config_utils.py +343 -0
  5. flwr/cli/example.py +4 -1
  6. flwr/cli/install.py +253 -0
  7. flwr/cli/log.py +182 -0
  8. flwr/{server/superlink/state → cli/login}/__init__.py +4 -10
  9. flwr/cli/login/login.py +88 -0
  10. flwr/cli/ls.py +327 -0
  11. flwr/cli/new/__init__.py +1 -0
  12. flwr/cli/new/new.py +210 -66
  13. flwr/cli/new/templates/app/.gitignore.tpl +163 -0
  14. flwr/cli/new/templates/app/LICENSE.tpl +202 -0
  15. flwr/cli/new/templates/app/README.baseline.md.tpl +127 -0
  16. flwr/cli/new/templates/app/README.flowertune.md.tpl +66 -0
  17. flwr/cli/new/templates/app/README.md.tpl +16 -32
  18. flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +1 -0
  19. flwr/cli/new/templates/app/code/__init__.py.tpl +1 -1
  20. flwr/cli/new/templates/app/code/client.baseline.py.tpl +58 -0
  21. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +55 -0
  22. flwr/cli/new/templates/app/code/client.jax.py.tpl +50 -0
  23. flwr/cli/new/templates/app/code/client.mlx.py.tpl +73 -0
  24. flwr/cli/new/templates/app/code/client.numpy.py.tpl +7 -7
  25. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +30 -21
  26. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +63 -0
  27. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +57 -1
  28. flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +36 -0
  29. flwr/cli/new/templates/app/code/flwr_tune/__init__.py +15 -0
  30. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +126 -0
  31. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +87 -0
  32. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +78 -0
  33. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +94 -0
  34. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +83 -0
  35. flwr/cli/new/templates/app/code/model.baseline.py.tpl +80 -0
  36. flwr/cli/new/templates/app/code/server.baseline.py.tpl +46 -0
  37. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +38 -0
  38. flwr/cli/new/templates/app/code/server.jax.py.tpl +26 -0
  39. flwr/cli/new/templates/app/code/server.mlx.py.tpl +31 -0
  40. flwr/cli/new/templates/app/code/server.numpy.py.tpl +22 -9
  41. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +21 -18
  42. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +36 -0
  43. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +29 -1
  44. flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +1 -0
  45. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +102 -0
  46. flwr/cli/new/templates/app/code/task.jax.py.tpl +57 -0
  47. flwr/cli/new/templates/app/code/task.mlx.py.tpl +102 -0
  48. flwr/cli/new/templates/app/code/task.numpy.py.tpl +7 -0
  49. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +29 -24
  50. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +67 -0
  51. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +53 -0
  52. flwr/cli/new/templates/app/code/utils.baseline.py.tpl +1 -0
  53. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +138 -0
  54. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +68 -0
  55. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +46 -0
  56. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +35 -0
  57. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +39 -0
  58. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +25 -12
  59. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +29 -14
  60. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +35 -0
  61. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +29 -14
  62. flwr/cli/run/__init__.py +1 -0
  63. flwr/cli/run/run.py +212 -34
  64. flwr/cli/stop.py +130 -0
  65. flwr/cli/utils.py +240 -5
  66. flwr/client/__init__.py +3 -2
  67. flwr/client/app.py +432 -255
  68. flwr/client/client.py +1 -11
  69. flwr/client/client_app.py +74 -13
  70. flwr/client/clientapp/__init__.py +22 -0
  71. flwr/client/clientapp/app.py +259 -0
  72. flwr/client/clientapp/clientappio_servicer.py +244 -0
  73. flwr/client/clientapp/utils.py +115 -0
  74. flwr/client/dpfedavg_numpy_client.py +7 -8
  75. flwr/client/grpc_adapter_client/__init__.py +15 -0
  76. flwr/client/grpc_adapter_client/connection.py +98 -0
  77. flwr/client/grpc_client/connection.py +21 -7
  78. flwr/client/grpc_rere_client/__init__.py +1 -1
  79. flwr/client/grpc_rere_client/client_interceptor.py +176 -0
  80. flwr/client/grpc_rere_client/connection.py +163 -56
  81. flwr/client/grpc_rere_client/grpc_adapter.py +167 -0
  82. flwr/client/heartbeat.py +74 -0
  83. flwr/client/message_handler/__init__.py +1 -1
  84. flwr/client/message_handler/message_handler.py +10 -11
  85. flwr/client/mod/__init__.py +5 -5
  86. flwr/client/mod/centraldp_mods.py +4 -2
  87. flwr/client/mod/comms_mods.py +5 -4
  88. flwr/client/mod/localdp_mod.py +10 -5
  89. flwr/client/mod/secure_aggregation/__init__.py +1 -1
  90. flwr/client/mod/secure_aggregation/secaggplus_mod.py +26 -26
  91. flwr/client/mod/utils.py +2 -4
  92. flwr/client/nodestate/__init__.py +26 -0
  93. flwr/client/nodestate/in_memory_nodestate.py +38 -0
  94. flwr/client/nodestate/nodestate.py +31 -0
  95. flwr/client/nodestate/nodestate_factory.py +38 -0
  96. flwr/client/numpy_client.py +8 -31
  97. flwr/client/rest_client/__init__.py +1 -1
  98. flwr/client/rest_client/connection.py +199 -176
  99. flwr/client/run_info_store.py +112 -0
  100. flwr/client/supernode/__init__.py +24 -0
  101. flwr/client/supernode/app.py +321 -0
  102. flwr/client/typing.py +1 -0
  103. flwr/common/__init__.py +17 -11
  104. flwr/common/address.py +47 -3
  105. flwr/common/args.py +153 -0
  106. flwr/common/auth_plugin/__init__.py +24 -0
  107. flwr/common/auth_plugin/auth_plugin.py +121 -0
  108. flwr/common/config.py +243 -0
  109. flwr/common/constant.py +135 -1
  110. flwr/common/context.py +32 -2
  111. flwr/common/date.py +22 -4
  112. flwr/common/differential_privacy.py +2 -2
  113. flwr/common/dp.py +2 -4
  114. flwr/common/exit_handlers.py +3 -3
  115. flwr/common/grpc.py +164 -5
  116. flwr/common/logger.py +230 -12
  117. flwr/common/message.py +191 -106
  118. flwr/common/object_ref.py +179 -44
  119. flwr/common/pyproject.py +1 -0
  120. flwr/common/record/__init__.py +2 -1
  121. flwr/common/record/configsrecord.py +58 -18
  122. flwr/common/record/metricsrecord.py +57 -17
  123. flwr/common/record/parametersrecord.py +88 -20
  124. flwr/common/record/recordset.py +153 -30
  125. flwr/common/record/typeddict.py +30 -55
  126. flwr/common/recordset_compat.py +31 -12
  127. flwr/common/retry_invoker.py +123 -30
  128. flwr/common/secure_aggregation/__init__.py +1 -1
  129. flwr/common/secure_aggregation/crypto/__init__.py +1 -1
  130. flwr/common/secure_aggregation/crypto/shamir.py +11 -11
  131. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +68 -4
  132. flwr/common/secure_aggregation/ndarrays_arithmetic.py +17 -17
  133. flwr/common/secure_aggregation/quantization.py +8 -8
  134. flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
  135. flwr/common/secure_aggregation/secaggplus_utils.py +10 -12
  136. flwr/common/serde.py +304 -23
  137. flwr/common/telemetry.py +65 -29
  138. flwr/common/typing.py +120 -19
  139. flwr/common/version.py +17 -3
  140. flwr/proto/clientappio_pb2.py +45 -0
  141. flwr/proto/clientappio_pb2.pyi +132 -0
  142. flwr/proto/clientappio_pb2_grpc.py +135 -0
  143. flwr/proto/clientappio_pb2_grpc.pyi +53 -0
  144. flwr/proto/exec_pb2.py +62 -0
  145. flwr/proto/exec_pb2.pyi +212 -0
  146. flwr/proto/exec_pb2_grpc.py +237 -0
  147. flwr/proto/exec_pb2_grpc.pyi +93 -0
  148. flwr/proto/fab_pb2.py +31 -0
  149. flwr/proto/fab_pb2.pyi +65 -0
  150. flwr/proto/fab_pb2_grpc.py +4 -0
  151. flwr/proto/fab_pb2_grpc.pyi +4 -0
  152. flwr/proto/fleet_pb2.py +42 -23
  153. flwr/proto/fleet_pb2.pyi +123 -1
  154. flwr/proto/fleet_pb2_grpc.py +170 -0
  155. flwr/proto/fleet_pb2_grpc.pyi +61 -0
  156. flwr/proto/grpcadapter_pb2.py +32 -0
  157. flwr/proto/grpcadapter_pb2.pyi +43 -0
  158. flwr/proto/grpcadapter_pb2_grpc.py +66 -0
  159. flwr/proto/grpcadapter_pb2_grpc.pyi +24 -0
  160. flwr/proto/log_pb2.py +29 -0
  161. flwr/proto/log_pb2.pyi +39 -0
  162. flwr/proto/log_pb2_grpc.py +4 -0
  163. flwr/proto/log_pb2_grpc.pyi +4 -0
  164. flwr/proto/message_pb2.py +41 -0
  165. flwr/proto/message_pb2.pyi +128 -0
  166. flwr/proto/message_pb2_grpc.py +4 -0
  167. flwr/proto/message_pb2_grpc.pyi +4 -0
  168. flwr/proto/node_pb2.py +2 -2
  169. flwr/proto/node_pb2.pyi +1 -4
  170. flwr/proto/recordset_pb2.py +35 -33
  171. flwr/proto/recordset_pb2.pyi +40 -14
  172. flwr/proto/run_pb2.py +64 -0
  173. flwr/proto/run_pb2.pyi +268 -0
  174. flwr/proto/run_pb2_grpc.py +4 -0
  175. flwr/proto/run_pb2_grpc.pyi +4 -0
  176. flwr/proto/serverappio_pb2.py +52 -0
  177. flwr/proto/{driver_pb2.pyi → serverappio_pb2.pyi} +62 -20
  178. flwr/proto/serverappio_pb2_grpc.py +410 -0
  179. flwr/proto/serverappio_pb2_grpc.pyi +160 -0
  180. flwr/proto/simulationio_pb2.py +38 -0
  181. flwr/proto/simulationio_pb2.pyi +65 -0
  182. flwr/proto/simulationio_pb2_grpc.py +239 -0
  183. flwr/proto/simulationio_pb2_grpc.pyi +94 -0
  184. flwr/proto/task_pb2.py +7 -8
  185. flwr/proto/task_pb2.pyi +8 -5
  186. flwr/proto/transport_pb2.py +8 -8
  187. flwr/proto/transport_pb2.pyi +9 -6
  188. flwr/server/__init__.py +2 -10
  189. flwr/server/app.py +579 -402
  190. flwr/server/client_manager.py +8 -6
  191. flwr/server/compat/app.py +6 -62
  192. flwr/server/compat/app_utils.py +14 -9
  193. flwr/server/compat/driver_client_proxy.py +25 -59
  194. flwr/server/compat/legacy_context.py +5 -4
  195. flwr/server/driver/__init__.py +2 -0
  196. flwr/server/driver/driver.py +36 -131
  197. flwr/server/driver/grpc_driver.py +220 -81
  198. flwr/server/driver/inmemory_driver.py +183 -0
  199. flwr/server/history.py +28 -29
  200. flwr/server/run_serverapp.py +15 -126
  201. flwr/server/server.py +50 -44
  202. flwr/server/server_app.py +59 -10
  203. flwr/server/serverapp/__init__.py +22 -0
  204. flwr/server/serverapp/app.py +256 -0
  205. flwr/server/serverapp_components.py +52 -0
  206. flwr/server/strategy/__init__.py +2 -2
  207. flwr/server/strategy/aggregate.py +37 -23
  208. flwr/server/strategy/bulyan.py +9 -9
  209. flwr/server/strategy/dp_adaptive_clipping.py +25 -25
  210. flwr/server/strategy/dp_fixed_clipping.py +23 -22
  211. flwr/server/strategy/dpfedavg_adaptive.py +8 -8
  212. flwr/server/strategy/dpfedavg_fixed.py +13 -12
  213. flwr/server/strategy/fault_tolerant_fedavg.py +11 -11
  214. flwr/server/strategy/fedadagrad.py +9 -9
  215. flwr/server/strategy/fedadam.py +20 -10
  216. flwr/server/strategy/fedavg.py +16 -16
  217. flwr/server/strategy/fedavg_android.py +17 -17
  218. flwr/server/strategy/fedavgm.py +9 -9
  219. flwr/server/strategy/fedmedian.py +5 -5
  220. flwr/server/strategy/fedopt.py +6 -6
  221. flwr/server/strategy/fedprox.py +7 -7
  222. flwr/server/strategy/fedtrimmedavg.py +8 -8
  223. flwr/server/strategy/fedxgb_bagging.py +12 -12
  224. flwr/server/strategy/fedxgb_cyclic.py +10 -10
  225. flwr/server/strategy/fedxgb_nn_avg.py +6 -6
  226. flwr/server/strategy/fedyogi.py +9 -9
  227. flwr/server/strategy/krum.py +9 -9
  228. flwr/server/strategy/qfedavg.py +16 -16
  229. flwr/server/strategy/strategy.py +10 -10
  230. flwr/server/superlink/driver/__init__.py +2 -2
  231. flwr/server/superlink/driver/serverappio_grpc.py +61 -0
  232. flwr/server/superlink/driver/serverappio_servicer.py +361 -0
  233. flwr/server/superlink/ffs/__init__.py +24 -0
  234. flwr/server/superlink/ffs/disk_ffs.py +108 -0
  235. flwr/server/superlink/ffs/ffs.py +79 -0
  236. flwr/server/superlink/ffs/ffs_factory.py +47 -0
  237. flwr/server/superlink/fleet/__init__.py +1 -1
  238. flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
  239. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +162 -0
  240. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  241. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +4 -2
  242. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -2
  243. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  244. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +5 -154
  245. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  246. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +120 -13
  247. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +228 -0
  248. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  249. flwr/server/superlink/fleet/message_handler/message_handler.py +156 -13
  250. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  251. flwr/server/superlink/fleet/rest_rere/rest_api.py +119 -81
  252. flwr/server/superlink/fleet/vce/__init__.py +1 -0
  253. flwr/server/superlink/fleet/vce/backend/__init__.py +4 -4
  254. flwr/server/superlink/fleet/vce/backend/backend.py +8 -9
  255. flwr/server/superlink/fleet/vce/backend/raybackend.py +87 -68
  256. flwr/server/superlink/fleet/vce/vce_api.py +208 -146
  257. flwr/server/superlink/linkstate/__init__.py +28 -0
  258. flwr/server/superlink/linkstate/in_memory_linkstate.py +569 -0
  259. flwr/server/superlink/linkstate/linkstate.py +376 -0
  260. flwr/server/superlink/{state/state_factory.py → linkstate/linkstate_factory.py} +19 -10
  261. flwr/server/superlink/linkstate/sqlite_linkstate.py +1196 -0
  262. flwr/server/superlink/linkstate/utils.py +399 -0
  263. flwr/server/superlink/simulation/__init__.py +15 -0
  264. flwr/server/superlink/simulation/simulationio_grpc.py +65 -0
  265. flwr/server/superlink/simulation/simulationio_servicer.py +186 -0
  266. flwr/server/superlink/utils.py +65 -0
  267. flwr/server/typing.py +2 -0
  268. flwr/server/utils/__init__.py +1 -1
  269. flwr/server/utils/tensorboard.py +5 -5
  270. flwr/server/utils/validator.py +40 -45
  271. flwr/server/workflow/default_workflows.py +70 -26
  272. flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -0
  273. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +40 -27
  274. flwr/simulation/__init__.py +12 -5
  275. flwr/simulation/app.py +247 -315
  276. flwr/simulation/legacy_app.py +404 -0
  277. flwr/simulation/ray_transport/__init__.py +1 -1
  278. flwr/simulation/ray_transport/ray_actor.py +42 -67
  279. flwr/simulation/ray_transport/ray_client_proxy.py +37 -17
  280. flwr/simulation/ray_transport/utils.py +1 -0
  281. flwr/simulation/run_simulation.py +306 -163
  282. flwr/simulation/simulationio_connection.py +89 -0
  283. flwr/superexec/__init__.py +15 -0
  284. flwr/superexec/app.py +59 -0
  285. flwr/superexec/deployment.py +188 -0
  286. flwr/superexec/exec_grpc.py +80 -0
  287. flwr/superexec/exec_servicer.py +231 -0
  288. flwr/superexec/exec_user_auth_interceptor.py +101 -0
  289. flwr/superexec/executor.py +96 -0
  290. flwr/superexec/simulation.py +124 -0
  291. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250115.dist-info}/METADATA +33 -26
  292. flwr_nightly-1.15.0.dev20250115.dist-info/RECORD +328 -0
  293. flwr_nightly-1.15.0.dev20250115.dist-info/entry_points.txt +12 -0
  294. flwr/cli/flower_toml.py +0 -140
  295. flwr/cli/new/templates/app/flower.toml.tpl +0 -13
  296. flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -2
  297. flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +0 -4
  298. flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -4
  299. flwr/client/node_state.py +0 -48
  300. flwr/client/node_state_tests.py +0 -65
  301. flwr/proto/driver_pb2.py +0 -44
  302. flwr/proto/driver_pb2_grpc.py +0 -169
  303. flwr/proto/driver_pb2_grpc.pyi +0 -66
  304. flwr/server/superlink/driver/driver_grpc.py +0 -54
  305. flwr/server/superlink/driver/driver_servicer.py +0 -129
  306. flwr/server/superlink/state/in_memory_state.py +0 -230
  307. flwr/server/superlink/state/sqlite_state.py +0 -630
  308. flwr/server/superlink/state/state.py +0 -154
  309. flwr_nightly-1.8.0.dev20240315.dist-info/RECORD +0 -211
  310. flwr_nightly-1.8.0.dev20240315.dist-info/entry_points.txt +0 -9
  311. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250115.dist-info}/LICENSE +0 -0
  312. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250115.dist-info}/WHEEL +0 -0
@@ -15,40 +15,38 @@
15
15
  """Run ServerApp."""
16
16
 
17
17
 
18
- import argparse
19
- import sys
20
- from logging import DEBUG, INFO, WARN
21
- from pathlib import Path
18
+ from logging import DEBUG, ERROR
22
19
  from typing import Optional
23
20
 
24
- from flwr.common import Context, EventType, RecordSet, event
25
- from flwr.common.logger import log, update_console_handler
21
+ from flwr.common import Context, EventType, event
22
+ from flwr.common.exit_handlers import register_exit_handlers
23
+ from flwr.common.logger import log
26
24
  from flwr.common.object_ref import load_app
27
25
 
28
- from .driver.driver import Driver
26
+ from .driver import Driver
29
27
  from .server_app import LoadServerAppError, ServerApp
30
28
 
31
29
 
32
30
  def run(
33
31
  driver: Driver,
32
+ context: Context,
34
33
  server_app_dir: str,
35
34
  server_app_attr: Optional[str] = None,
36
35
  loaded_server_app: Optional[ServerApp] = None,
37
- ) -> None:
36
+ ) -> Context:
38
37
  """Run ServerApp with a given Driver."""
39
38
  if not (server_app_attr is None) ^ (loaded_server_app is None):
40
39
  raise ValueError(
41
40
  "Either `server_app_attr` or `loaded_server_app` should be set "
42
- "but not both. "
41
+ "but not both."
43
42
  )
44
43
 
45
- if server_app_dir is not None:
46
- sys.path.insert(0, server_app_dir)
47
-
48
44
  # Load ServerApp if needed
49
45
  def _load() -> ServerApp:
50
46
  if server_app_attr:
51
- server_app: ServerApp = load_app(server_app_attr, LoadServerAppError)
47
+ server_app: ServerApp = load_app(
48
+ server_app_attr, LoadServerAppError, server_app_dir
49
+ )
52
50
 
53
51
  if not isinstance(server_app, ServerApp):
54
52
  raise LoadServerAppError(
@@ -61,127 +59,18 @@ def run(
61
59
 
62
60
  server_app = _load()
63
61
 
64
- # Initialize Context
65
- context = Context(state=RecordSet())
66
-
67
62
  # Call ServerApp
68
63
  server_app(driver=driver, context=context)
69
64
 
70
65
  log(DEBUG, "ServerApp finished running.")
66
+ return context
71
67
 
72
68
 
73
69
  def run_server_app() -> None:
74
70
  """Run Flower server app."""
75
71
  event(EventType.RUN_SERVER_APP_ENTER)
76
-
77
- args = _parse_args_run_server_app().parse_args()
78
-
79
- update_console_handler(
80
- level=DEBUG if args.verbose else INFO,
81
- timestamps=args.verbose,
82
- colored=True,
83
- )
84
-
85
- # Obtain certificates
86
- if args.insecure:
87
- if args.root_certificates is not None:
88
- sys.exit(
89
- "Conflicting options: The '--insecure' flag disables HTTPS, "
90
- "but '--root-certificates' was also specified. Please remove "
91
- "the '--root-certificates' option when running in insecure mode, "
92
- "or omit '--insecure' to use HTTPS."
93
- )
94
- log(
95
- WARN,
96
- "Option `--insecure` was set. "
97
- "Starting insecure HTTP client connected to %s.",
98
- args.server,
99
- )
100
- root_certificates = None
101
- else:
102
- # Load the certificates if provided, or load the system certificates
103
- cert_path = args.root_certificates
104
- if cert_path is None:
105
- root_certificates = None
106
- else:
107
- root_certificates = Path(cert_path).read_bytes()
108
- log(
109
- DEBUG,
110
- "Starting secure HTTPS client connected to %s "
111
- "with the following certificates: %s.",
112
- args.server,
113
- cert_path,
114
- )
115
-
116
- log(
117
- DEBUG,
118
- "Flower will load ServerApp `%s`",
119
- getattr(args, "server-app"),
120
- )
121
-
122
72
  log(
123
- DEBUG,
124
- "root_certificates: `%s`",
125
- root_certificates,
73
+ ERROR,
74
+ "The command `flower-server-app` has been replaced by `flwr run`.",
126
75
  )
127
-
128
- server_app_dir = args.dir
129
- server_app_attr = getattr(args, "server-app")
130
-
131
- # Initialize Driver
132
- driver = Driver(
133
- driver_service_address=args.server,
134
- root_certificates=root_certificates,
135
- )
136
-
137
- # Run the Server App with the Driver
138
- run(driver=driver, server_app_dir=server_app_dir, server_app_attr=server_app_attr)
139
-
140
- # Clean up
141
- driver.close()
142
-
143
- event(EventType.RUN_SERVER_APP_LEAVE)
144
-
145
-
146
- def _parse_args_run_server_app() -> argparse.ArgumentParser:
147
- """Parse flower-server-app command line arguments."""
148
- parser = argparse.ArgumentParser(
149
- description="Start a Flower server app",
150
- )
151
-
152
- parser.add_argument(
153
- "server-app",
154
- help="For example: `server:app` or `project.package.module:wrapper.app`",
155
- )
156
- parser.add_argument(
157
- "--insecure",
158
- action="store_true",
159
- help="Run the server app without HTTPS. By default, the app runs with "
160
- "HTTPS enabled. Use this flag only if you understand the risks.",
161
- )
162
- parser.add_argument(
163
- "--verbose",
164
- action="store_true",
165
- help="Set the logging to `DEBUG`.",
166
- )
167
- parser.add_argument(
168
- "--root-certificates",
169
- metavar="ROOT_CERT",
170
- type=str,
171
- help="Specifies the path to the PEM-encoded root certificate file for "
172
- "establishing secure HTTPS connections.",
173
- )
174
- parser.add_argument(
175
- "--server",
176
- default="0.0.0.0:9091",
177
- help="Server address",
178
- )
179
- parser.add_argument(
180
- "--dir",
181
- default="",
182
- help="Add specified directory to the PYTHONPATH and load Flower "
183
- "app from there."
184
- " Default: current working directory.",
185
- )
186
-
187
- return parser
76
+ register_exit_handlers(event_type=EventType.RUN_SERVER_APP_LEAVE)
flwr/server/server.py CHANGED
@@ -19,7 +19,7 @@ import concurrent.futures
19
19
  import io
20
20
  import timeit
21
21
  from logging import INFO, WARN
22
- from typing import Dict, List, Optional, Tuple, Union
22
+ from typing import Optional, Union
23
23
 
24
24
  from flwr.common import (
25
25
  Code,
@@ -41,17 +41,17 @@ from flwr.server.strategy import FedAvg, Strategy
41
41
 
42
42
  from .server_config import ServerConfig
43
43
 
44
- FitResultsAndFailures = Tuple[
45
- List[Tuple[ClientProxy, FitRes]],
46
- List[Union[Tuple[ClientProxy, FitRes], BaseException]],
44
+ FitResultsAndFailures = tuple[
45
+ list[tuple[ClientProxy, FitRes]],
46
+ list[Union[tuple[ClientProxy, FitRes], BaseException]],
47
47
  ]
48
- EvaluateResultsAndFailures = Tuple[
49
- List[Tuple[ClientProxy, EvaluateRes]],
50
- List[Union[Tuple[ClientProxy, EvaluateRes], BaseException]],
48
+ EvaluateResultsAndFailures = tuple[
49
+ list[tuple[ClientProxy, EvaluateRes]],
50
+ list[Union[tuple[ClientProxy, EvaluateRes], BaseException]],
51
51
  ]
52
- ReconnectResultsAndFailures = Tuple[
53
- List[Tuple[ClientProxy, DisconnectRes]],
54
- List[Union[Tuple[ClientProxy, DisconnectRes], BaseException]],
52
+ ReconnectResultsAndFailures = tuple[
53
+ list[tuple[ClientProxy, DisconnectRes]],
54
+ list[Union[tuple[ClientProxy, DisconnectRes], BaseException]],
55
55
  ]
56
56
 
57
57
 
@@ -84,14 +84,14 @@ class Server:
84
84
  return self._client_manager
85
85
 
86
86
  # pylint: disable=too-many-locals
87
- def fit(self, num_rounds: int, timeout: Optional[float]) -> Tuple[History, float]:
87
+ def fit(self, num_rounds: int, timeout: Optional[float]) -> tuple[History, float]:
88
88
  """Run federated averaging for a number of rounds."""
89
89
  history = History()
90
90
 
91
91
  # Initialize parameters
92
92
  log(INFO, "[INIT]")
93
93
  self.parameters = self._get_initial_parameters(server_round=0, timeout=timeout)
94
- log(INFO, "Evaluating initial global parameters")
94
+ log(INFO, "Starting evaluation of initial global parameters")
95
95
  res = self.strategy.evaluate(0, parameters=self.parameters)
96
96
  if res is not None:
97
97
  log(
@@ -102,6 +102,8 @@ class Server:
102
102
  )
103
103
  history.add_loss_centralized(server_round=0, loss=res[0])
104
104
  history.add_metrics_centralized(server_round=0, metrics=res[1])
105
+ else:
106
+ log(INFO, "Evaluation returned no results (`None`)")
105
107
 
106
108
  # Run federated learning for num_rounds
107
109
  start_time = timeit.default_timer()
@@ -161,7 +163,7 @@ class Server:
161
163
  server_round: int,
162
164
  timeout: Optional[float],
163
165
  ) -> Optional[
164
- Tuple[Optional[float], Dict[str, Scalar], EvaluateResultsAndFailures]
166
+ tuple[Optional[float], dict[str, Scalar], EvaluateResultsAndFailures]
165
167
  ]:
166
168
  """Validate current global model on a number of clients."""
167
169
  # Get clients and their respective instructions from strategy
@@ -195,9 +197,9 @@ class Server:
195
197
  )
196
198
 
197
199
  # Aggregate the evaluation results
198
- aggregated_result: Tuple[
200
+ aggregated_result: tuple[
199
201
  Optional[float],
200
- Dict[str, Scalar],
202
+ dict[str, Scalar],
201
203
  ] = self.strategy.aggregate_evaluate(server_round, results, failures)
202
204
 
203
205
  loss_aggregated, metrics_aggregated = aggregated_result
@@ -208,7 +210,7 @@ class Server:
208
210
  server_round: int,
209
211
  timeout: Optional[float],
210
212
  ) -> Optional[
211
- Tuple[Optional[Parameters], Dict[str, Scalar], FitResultsAndFailures]
213
+ tuple[Optional[Parameters], dict[str, Scalar], FitResultsAndFailures]
212
214
  ]:
213
215
  """Perform a single round of federated averaging."""
214
216
  # Get clients and their respective instructions from strategy
@@ -243,9 +245,9 @@ class Server:
243
245
  )
244
246
 
245
247
  # Aggregate training results
246
- aggregated_result: Tuple[
248
+ aggregated_result: tuple[
247
249
  Optional[Parameters],
248
- Dict[str, Scalar],
250
+ dict[str, Scalar],
249
251
  ] = self.strategy.aggregate_fit(server_round, results, failures)
250
252
 
251
253
  parameters_aggregated, metrics_aggregated = aggregated_result
@@ -282,12 +284,19 @@ class Server:
282
284
  get_parameters_res = random_client.get_parameters(
283
285
  ins=ins, timeout=timeout, group_id=server_round
284
286
  )
285
- log(INFO, "Received initial parameters from one random client")
287
+ if get_parameters_res.status.code == Code.OK:
288
+ log(INFO, "Received initial parameters from one random client")
289
+ else:
290
+ log(
291
+ WARN,
292
+ "Failed to receive initial parameters from the client."
293
+ " Empty initial parameters will be used.",
294
+ )
286
295
  return get_parameters_res.parameters
287
296
 
288
297
 
289
298
  def reconnect_clients(
290
- client_instructions: List[Tuple[ClientProxy, ReconnectIns]],
299
+ client_instructions: list[tuple[ClientProxy, ReconnectIns]],
291
300
  max_workers: Optional[int],
292
301
  timeout: Optional[float],
293
302
  ) -> ReconnectResultsAndFailures:
@@ -303,8 +312,8 @@ def reconnect_clients(
303
312
  )
304
313
 
305
314
  # Gather results
306
- results: List[Tuple[ClientProxy, DisconnectRes]] = []
307
- failures: List[Union[Tuple[ClientProxy, DisconnectRes], BaseException]] = []
315
+ results: list[tuple[ClientProxy, DisconnectRes]] = []
316
+ failures: list[Union[tuple[ClientProxy, DisconnectRes], BaseException]] = []
308
317
  for future in finished_fs:
309
318
  failure = future.exception()
310
319
  if failure is not None:
@@ -319,7 +328,7 @@ def reconnect_client(
319
328
  client: ClientProxy,
320
329
  reconnect: ReconnectIns,
321
330
  timeout: Optional[float],
322
- ) -> Tuple[ClientProxy, DisconnectRes]:
331
+ ) -> tuple[ClientProxy, DisconnectRes]:
323
332
  """Instruct client to disconnect and (optionally) reconnect later."""
324
333
  disconnect = client.reconnect(
325
334
  reconnect,
@@ -330,7 +339,7 @@ def reconnect_client(
330
339
 
331
340
 
332
341
  def fit_clients(
333
- client_instructions: List[Tuple[ClientProxy, FitIns]],
342
+ client_instructions: list[tuple[ClientProxy, FitIns]],
334
343
  max_workers: Optional[int],
335
344
  timeout: Optional[float],
336
345
  group_id: int,
@@ -347,8 +356,8 @@ def fit_clients(
347
356
  )
348
357
 
349
358
  # Gather results
350
- results: List[Tuple[ClientProxy, FitRes]] = []
351
- failures: List[Union[Tuple[ClientProxy, FitRes], BaseException]] = []
359
+ results: list[tuple[ClientProxy, FitRes]] = []
360
+ failures: list[Union[tuple[ClientProxy, FitRes], BaseException]] = []
352
361
  for future in finished_fs:
353
362
  _handle_finished_future_after_fit(
354
363
  future=future, results=results, failures=failures
@@ -358,7 +367,7 @@ def fit_clients(
358
367
 
359
368
  def fit_client(
360
369
  client: ClientProxy, ins: FitIns, timeout: Optional[float], group_id: int
361
- ) -> Tuple[ClientProxy, FitRes]:
370
+ ) -> tuple[ClientProxy, FitRes]:
362
371
  """Refine parameters on a single client."""
363
372
  fit_res = client.fit(ins, timeout=timeout, group_id=group_id)
364
373
  return client, fit_res
@@ -366,8 +375,8 @@ def fit_client(
366
375
 
367
376
  def _handle_finished_future_after_fit(
368
377
  future: concurrent.futures.Future, # type: ignore
369
- results: List[Tuple[ClientProxy, FitRes]],
370
- failures: List[Union[Tuple[ClientProxy, FitRes], BaseException]],
378
+ results: list[tuple[ClientProxy, FitRes]],
379
+ failures: list[Union[tuple[ClientProxy, FitRes], BaseException]],
371
380
  ) -> None:
372
381
  """Convert finished future into either a result or a failure."""
373
382
  # Check if there was an exception
@@ -377,7 +386,7 @@ def _handle_finished_future_after_fit(
377
386
  return
378
387
 
379
388
  # Successfully received a result from a client
380
- result: Tuple[ClientProxy, FitRes] = future.result()
389
+ result: tuple[ClientProxy, FitRes] = future.result()
381
390
  _, res = result
382
391
 
383
392
  # Check result status code
@@ -390,7 +399,7 @@ def _handle_finished_future_after_fit(
390
399
 
391
400
 
392
401
  def evaluate_clients(
393
- client_instructions: List[Tuple[ClientProxy, EvaluateIns]],
402
+ client_instructions: list[tuple[ClientProxy, EvaluateIns]],
394
403
  max_workers: Optional[int],
395
404
  timeout: Optional[float],
396
405
  group_id: int,
@@ -407,8 +416,8 @@ def evaluate_clients(
407
416
  )
408
417
 
409
418
  # Gather results
410
- results: List[Tuple[ClientProxy, EvaluateRes]] = []
411
- failures: List[Union[Tuple[ClientProxy, EvaluateRes], BaseException]] = []
419
+ results: list[tuple[ClientProxy, EvaluateRes]] = []
420
+ failures: list[Union[tuple[ClientProxy, EvaluateRes], BaseException]] = []
412
421
  for future in finished_fs:
413
422
  _handle_finished_future_after_evaluate(
414
423
  future=future, results=results, failures=failures
@@ -421,7 +430,7 @@ def evaluate_client(
421
430
  ins: EvaluateIns,
422
431
  timeout: Optional[float],
423
432
  group_id: int,
424
- ) -> Tuple[ClientProxy, EvaluateRes]:
433
+ ) -> tuple[ClientProxy, EvaluateRes]:
425
434
  """Evaluate parameters on a single client."""
426
435
  evaluate_res = client.evaluate(ins, timeout=timeout, group_id=group_id)
427
436
  return client, evaluate_res
@@ -429,8 +438,8 @@ def evaluate_client(
429
438
 
430
439
  def _handle_finished_future_after_evaluate(
431
440
  future: concurrent.futures.Future, # type: ignore
432
- results: List[Tuple[ClientProxy, EvaluateRes]],
433
- failures: List[Union[Tuple[ClientProxy, EvaluateRes], BaseException]],
441
+ results: list[tuple[ClientProxy, EvaluateRes]],
442
+ failures: list[Union[tuple[ClientProxy, EvaluateRes], BaseException]],
434
443
  ) -> None:
435
444
  """Convert finished future into either a result or a failure."""
436
445
  # Check if there was an exception
@@ -440,7 +449,7 @@ def _handle_finished_future_after_evaluate(
440
449
  return
441
450
 
442
451
  # Successfully received a result from a client
443
- result: Tuple[ClientProxy, EvaluateRes] = future.result()
452
+ result: tuple[ClientProxy, EvaluateRes] = future.result()
444
453
  _, res = result
445
454
 
446
455
  # Check result status code
@@ -457,7 +466,7 @@ def init_defaults(
457
466
  config: Optional[ServerConfig],
458
467
  strategy: Optional[Strategy],
459
468
  client_manager: Optional[ClientManager],
460
- ) -> Tuple[Server, ServerConfig]:
469
+ ) -> tuple[Server, ServerConfig]:
461
470
  """Create server instance if none was given."""
462
471
  if server is None:
463
472
  if client_manager is None:
@@ -486,12 +495,9 @@ def run_fl(
486
495
 
487
496
  log(INFO, "")
488
497
  log(INFO, "[SUMMARY]")
489
- log(INFO, "Run finished %s rounds in %.2fs", config.num_rounds, elapsed_time)
490
- for idx, line in enumerate(io.StringIO(str(hist))):
491
- if idx == 0:
492
- log(INFO, "%s", line.strip("\n"))
493
- else:
494
- log(INFO, "\t%s", line.strip("\n"))
498
+ log(INFO, "Run finished %s round(s) in %.2fs", config.num_rounds, elapsed_time)
499
+ for line in io.StringIO(str(hist)):
500
+ log(INFO, "\t%s", line.strip("\n"))
495
501
  log(INFO, "")
496
502
 
497
503
  # Graceful shutdown
flwr/server/server_app.py CHANGED
@@ -17,7 +17,11 @@
17
17
 
18
18
  from typing import Callable, Optional
19
19
 
20
- from flwr.common import Context, RecordSet
20
+ from flwr.common import Context
21
+ from flwr.common.logger import (
22
+ warn_deprecated_feature_with_example,
23
+ warn_preview_feature,
24
+ )
21
25
  from flwr.server.strategy import Strategy
22
26
 
23
27
  from .client_manager import ClientManager
@@ -25,7 +29,20 @@ from .compat import start_driver
25
29
  from .driver import Driver
26
30
  from .server import Server
27
31
  from .server_config import ServerConfig
28
- from .typing import ServerAppCallable
32
+ from .typing import ServerAppCallable, ServerFn
33
+
34
+ SERVER_FN_USAGE_EXAMPLE = """
35
+
36
+ def server_fn(context: Context):
37
+ server_config = ServerConfig(num_rounds=3)
38
+ strategy = FedAvg()
39
+ return ServerAppComponents(
40
+ strategy=strategy,
41
+ server_config=server_config,
42
+ )
43
+
44
+ app = ServerApp(server_fn=server_fn)
45
+ """
29
46
 
30
47
 
31
48
  class ServerApp:
@@ -35,13 +52,15 @@ class ServerApp:
35
52
  --------
36
53
  Use the `ServerApp` with an existing `Strategy`:
37
54
 
38
- >>> server_config = ServerConfig(num_rounds=3)
39
- >>> strategy = FedAvg()
55
+ >>> def server_fn(context: Context):
56
+ >>> server_config = ServerConfig(num_rounds=3)
57
+ >>> strategy = FedAvg()
58
+ >>> return ServerAppComponents(
59
+ >>> strategy=strategy,
60
+ >>> server_config=server_config,
61
+ >>> )
40
62
  >>>
41
- >>> app = ServerApp()
42
- >>> server_config=server_config,
43
- >>> strategy=strategy,
44
- >>> )
63
+ >>> app = ServerApp(server_fn=server_fn)
45
64
 
46
65
  Use the `ServerApp` with a custom main function:
47
66
 
@@ -52,23 +71,52 @@ class ServerApp:
52
71
  >>> print("ServerApp running")
53
72
  """
54
73
 
74
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
55
75
  def __init__(
56
76
  self,
57
77
  server: Optional[Server] = None,
58
78
  config: Optional[ServerConfig] = None,
59
79
  strategy: Optional[Strategy] = None,
60
80
  client_manager: Optional[ClientManager] = None,
81
+ server_fn: Optional[ServerFn] = None,
61
82
  ) -> None:
83
+ if any([server, config, strategy, client_manager]):
84
+ warn_deprecated_feature_with_example(
85
+ deprecation_message="Passing either `server`, `config`, `strategy` or "
86
+ "`client_manager` directly to the ServerApp "
87
+ "constructor is deprecated.",
88
+ example_message="Pass `ServerApp` arguments wrapped "
89
+ "in a `flwr.server.ServerAppComponents` object that gets "
90
+ "returned by a function passed as the `server_fn` argument "
91
+ "to the `ServerApp` constructor. For example: ",
92
+ code_example=SERVER_FN_USAGE_EXAMPLE,
93
+ )
94
+
95
+ if server_fn:
96
+ raise ValueError(
97
+ "Passing `server_fn` is incompatible with passing the "
98
+ "other arguments (now deprecated) to ServerApp. "
99
+ "Use `server_fn` exclusively."
100
+ )
101
+
62
102
  self._server = server
63
103
  self._config = config
64
104
  self._strategy = strategy
65
105
  self._client_manager = client_manager
106
+ self._server_fn = server_fn
66
107
  self._main: Optional[ServerAppCallable] = None
67
108
 
68
109
  def __call__(self, driver: Driver, context: Context) -> None:
69
110
  """Execute `ServerApp`."""
70
111
  # Compatibility mode
71
112
  if not self._main:
113
+ if self._server_fn:
114
+ # Execute server_fn()
115
+ components = self._server_fn(context)
116
+ self._server = components.server
117
+ self._config = components.config
118
+ self._strategy = components.strategy
119
+ self._client_manager = components.client_manager
72
120
  start_driver(
73
121
  server=self._server,
74
122
  config=self._config,
@@ -79,7 +127,6 @@ class ServerApp:
79
127
  return
80
128
 
81
129
  # New execution mode
82
- context = Context(state=RecordSet())
83
130
  self._main(driver, context)
84
131
 
85
132
  def main(self) -> Callable[[ServerAppCallable], ServerAppCallable]:
@@ -105,7 +152,7 @@ class ServerApp:
105
152
  >>> server_config = ServerConfig(num_rounds=3)
106
153
  >>> strategy = FedAvg()
107
154
  >>>
108
- >>> app = ServerApp()
155
+ >>> app = ServerApp(
109
156
  >>> server_config=server_config,
110
157
  >>> strategy=strategy,
111
158
  >>> )
@@ -120,6 +167,8 @@ class ServerApp:
120
167
  """,
121
168
  )
122
169
 
170
+ warn_preview_feature("ServerApp-register-main-function")
171
+
123
172
  # Register provided function with the ServerApp object
124
173
  self._main = main_fn
125
174
 
@@ -0,0 +1,22 @@
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Flower AppIO service."""
16
+
17
+
18
+ from .app import flwr_serverapp as flwr_serverapp
19
+
20
+ __all__ = [
21
+ "flwr_serverapp",
22
+ ]