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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (311) 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 +132 -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 +298 -19
  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 +1 -1
  169. flwr/proto/recordset_pb2.py +35 -33
  170. flwr/proto/recordset_pb2.pyi +40 -14
  171. flwr/proto/run_pb2.py +64 -0
  172. flwr/proto/run_pb2.pyi +268 -0
  173. flwr/proto/run_pb2_grpc.py +4 -0
  174. flwr/proto/run_pb2_grpc.pyi +4 -0
  175. flwr/proto/serverappio_pb2.py +52 -0
  176. flwr/proto/{driver_pb2.pyi → serverappio_pb2.pyi} +62 -20
  177. flwr/proto/serverappio_pb2_grpc.py +410 -0
  178. flwr/proto/serverappio_pb2_grpc.pyi +160 -0
  179. flwr/proto/simulationio_pb2.py +38 -0
  180. flwr/proto/simulationio_pb2.pyi +65 -0
  181. flwr/proto/simulationio_pb2_grpc.py +239 -0
  182. flwr/proto/simulationio_pb2_grpc.pyi +94 -0
  183. flwr/proto/task_pb2.py +7 -8
  184. flwr/proto/task_pb2.pyi +8 -5
  185. flwr/proto/transport_pb2.py +8 -8
  186. flwr/proto/transport_pb2.pyi +9 -6
  187. flwr/server/__init__.py +2 -10
  188. flwr/server/app.py +579 -402
  189. flwr/server/client_manager.py +8 -6
  190. flwr/server/compat/app.py +6 -62
  191. flwr/server/compat/app_utils.py +14 -8
  192. flwr/server/compat/driver_client_proxy.py +25 -58
  193. flwr/server/compat/legacy_context.py +5 -4
  194. flwr/server/driver/__init__.py +2 -0
  195. flwr/server/driver/driver.py +36 -131
  196. flwr/server/driver/grpc_driver.py +217 -81
  197. flwr/server/driver/inmemory_driver.py +182 -0
  198. flwr/server/history.py +28 -29
  199. flwr/server/run_serverapp.py +15 -126
  200. flwr/server/server.py +50 -44
  201. flwr/server/server_app.py +59 -10
  202. flwr/server/serverapp/__init__.py +22 -0
  203. flwr/server/serverapp/app.py +256 -0
  204. flwr/server/serverapp_components.py +52 -0
  205. flwr/server/strategy/__init__.py +2 -2
  206. flwr/server/strategy/aggregate.py +37 -23
  207. flwr/server/strategy/bulyan.py +9 -9
  208. flwr/server/strategy/dp_adaptive_clipping.py +25 -25
  209. flwr/server/strategy/dp_fixed_clipping.py +23 -22
  210. flwr/server/strategy/dpfedavg_adaptive.py +8 -8
  211. flwr/server/strategy/dpfedavg_fixed.py +13 -12
  212. flwr/server/strategy/fault_tolerant_fedavg.py +11 -11
  213. flwr/server/strategy/fedadagrad.py +9 -9
  214. flwr/server/strategy/fedadam.py +20 -10
  215. flwr/server/strategy/fedavg.py +16 -16
  216. flwr/server/strategy/fedavg_android.py +17 -17
  217. flwr/server/strategy/fedavgm.py +9 -9
  218. flwr/server/strategy/fedmedian.py +5 -5
  219. flwr/server/strategy/fedopt.py +6 -6
  220. flwr/server/strategy/fedprox.py +7 -7
  221. flwr/server/strategy/fedtrimmedavg.py +8 -8
  222. flwr/server/strategy/fedxgb_bagging.py +12 -12
  223. flwr/server/strategy/fedxgb_cyclic.py +10 -10
  224. flwr/server/strategy/fedxgb_nn_avg.py +6 -6
  225. flwr/server/strategy/fedyogi.py +9 -9
  226. flwr/server/strategy/krum.py +9 -9
  227. flwr/server/strategy/qfedavg.py +16 -16
  228. flwr/server/strategy/strategy.py +10 -10
  229. flwr/server/superlink/driver/__init__.py +2 -2
  230. flwr/server/superlink/driver/serverappio_grpc.py +61 -0
  231. flwr/server/superlink/driver/serverappio_servicer.py +363 -0
  232. flwr/server/superlink/ffs/__init__.py +24 -0
  233. flwr/server/superlink/ffs/disk_ffs.py +108 -0
  234. flwr/server/superlink/ffs/ffs.py +79 -0
  235. flwr/server/superlink/ffs/ffs_factory.py +47 -0
  236. flwr/server/superlink/fleet/__init__.py +1 -1
  237. flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
  238. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +162 -0
  239. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  240. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +4 -2
  241. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -2
  242. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  243. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +5 -154
  244. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  245. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +120 -13
  246. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +228 -0
  247. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  248. flwr/server/superlink/fleet/message_handler/message_handler.py +153 -9
  249. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  250. flwr/server/superlink/fleet/rest_rere/rest_api.py +119 -81
  251. flwr/server/superlink/fleet/vce/__init__.py +1 -0
  252. flwr/server/superlink/fleet/vce/backend/__init__.py +4 -4
  253. flwr/server/superlink/fleet/vce/backend/backend.py +8 -9
  254. flwr/server/superlink/fleet/vce/backend/raybackend.py +87 -68
  255. flwr/server/superlink/fleet/vce/vce_api.py +208 -146
  256. flwr/server/superlink/linkstate/__init__.py +28 -0
  257. flwr/server/superlink/linkstate/in_memory_linkstate.py +581 -0
  258. flwr/server/superlink/linkstate/linkstate.py +389 -0
  259. flwr/server/superlink/{state/state_factory.py → linkstate/linkstate_factory.py} +19 -10
  260. flwr/server/superlink/linkstate/sqlite_linkstate.py +1236 -0
  261. flwr/server/superlink/linkstate/utils.py +389 -0
  262. flwr/server/superlink/simulation/__init__.py +15 -0
  263. flwr/server/superlink/simulation/simulationio_grpc.py +65 -0
  264. flwr/server/superlink/simulation/simulationio_servicer.py +186 -0
  265. flwr/server/superlink/utils.py +65 -0
  266. flwr/server/typing.py +2 -0
  267. flwr/server/utils/__init__.py +1 -1
  268. flwr/server/utils/tensorboard.py +5 -5
  269. flwr/server/utils/validator.py +31 -11
  270. flwr/server/workflow/default_workflows.py +70 -26
  271. flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -0
  272. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +40 -27
  273. flwr/simulation/__init__.py +12 -5
  274. flwr/simulation/app.py +247 -315
  275. flwr/simulation/legacy_app.py +402 -0
  276. flwr/simulation/ray_transport/__init__.py +1 -1
  277. flwr/simulation/ray_transport/ray_actor.py +42 -67
  278. flwr/simulation/ray_transport/ray_client_proxy.py +37 -17
  279. flwr/simulation/ray_transport/utils.py +1 -0
  280. flwr/simulation/run_simulation.py +306 -163
  281. flwr/simulation/simulationio_connection.py +89 -0
  282. flwr/superexec/__init__.py +15 -0
  283. flwr/superexec/app.py +59 -0
  284. flwr/superexec/deployment.py +188 -0
  285. flwr/superexec/exec_grpc.py +80 -0
  286. flwr/superexec/exec_servicer.py +231 -0
  287. flwr/superexec/exec_user_auth_interceptor.py +101 -0
  288. flwr/superexec/executor.py +96 -0
  289. flwr/superexec/simulation.py +124 -0
  290. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/METADATA +33 -26
  291. flwr_nightly-1.15.0.dev20250114.dist-info/RECORD +328 -0
  292. flwr_nightly-1.15.0.dev20250114.dist-info/entry_points.txt +12 -0
  293. flwr/cli/flower_toml.py +0 -140
  294. flwr/cli/new/templates/app/flower.toml.tpl +0 -13
  295. flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -2
  296. flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +0 -4
  297. flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -4
  298. flwr/client/node_state.py +0 -48
  299. flwr/client/node_state_tests.py +0 -65
  300. flwr/proto/driver_pb2.py +0 -44
  301. flwr/proto/driver_pb2_grpc.py +0 -169
  302. flwr/proto/driver_pb2_grpc.pyi +0 -66
  303. flwr/server/superlink/driver/driver_grpc.py +0 -54
  304. flwr/server/superlink/driver/driver_servicer.py +0 -129
  305. flwr/server/superlink/state/in_memory_state.py +0 -230
  306. flwr/server/superlink/state/sqlite_state.py +0 -630
  307. flwr/server/superlink/state/state.py +0 -154
  308. flwr_nightly-1.8.0.dev20240315.dist-info/RECORD +0 -211
  309. flwr_nightly-1.8.0.dev20240315.dist-info/entry_points.txt +0 -9
  310. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/LICENSE +0 -0
  311. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/WHEEL +0 -0
@@ -0,0 +1,112 @@
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
+ """Deprecated Run Info Store."""
16
+
17
+
18
+ from dataclasses import dataclass
19
+ from pathlib import Path
20
+ from typing import Optional
21
+
22
+ from flwr.common import Context, RecordSet
23
+ from flwr.common.config import (
24
+ get_fused_config,
25
+ get_fused_config_from_dir,
26
+ get_fused_config_from_fab,
27
+ )
28
+ from flwr.common.typing import Fab, Run, UserConfig
29
+
30
+
31
+ @dataclass()
32
+ class RunInfo:
33
+ """Contains the Context and initial run_config of a Run."""
34
+
35
+ context: Context
36
+ initial_run_config: UserConfig
37
+
38
+
39
+ class DeprecatedRunInfoStore:
40
+ """State of a node where client nodes execute runs."""
41
+
42
+ def __init__(
43
+ self,
44
+ node_id: int,
45
+ node_config: UserConfig,
46
+ ) -> None:
47
+ self.node_id = node_id
48
+ self.node_config = node_config
49
+ self.run_infos: dict[int, RunInfo] = {}
50
+
51
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
52
+ def register_context(
53
+ self,
54
+ run_id: int,
55
+ run: Optional[Run] = None,
56
+ flwr_path: Optional[Path] = None,
57
+ app_dir: Optional[str] = None,
58
+ fab: Optional[Fab] = None,
59
+ ) -> None:
60
+ """Register new run context for this node."""
61
+ if run_id not in self.run_infos:
62
+ initial_run_config = {}
63
+ if app_dir:
64
+ # Load from app directory
65
+ app_path = Path(app_dir)
66
+ if app_path.is_dir():
67
+ override_config = run.override_config if run else {}
68
+ initial_run_config = get_fused_config_from_dir(
69
+ app_path, override_config
70
+ )
71
+ else:
72
+ raise ValueError("The specified `app_dir` must be a directory.")
73
+ else:
74
+ if run:
75
+ if fab:
76
+ # Load pyproject.toml from FAB file and fuse
77
+ initial_run_config = get_fused_config_from_fab(fab.content, run)
78
+ else:
79
+ # Load pyproject.toml from installed FAB and fuse
80
+ initial_run_config = get_fused_config(run, flwr_path)
81
+ else:
82
+ initial_run_config = {}
83
+ self.run_infos[run_id] = RunInfo(
84
+ initial_run_config=initial_run_config,
85
+ context=Context(
86
+ run_id=run_id,
87
+ node_id=self.node_id,
88
+ node_config=self.node_config,
89
+ state=RecordSet(),
90
+ run_config=initial_run_config.copy(),
91
+ ),
92
+ )
93
+
94
+ def retrieve_context(self, run_id: int) -> Context:
95
+ """Get run context given a run_id."""
96
+ if run_id in self.run_infos:
97
+ return self.run_infos[run_id].context
98
+
99
+ raise RuntimeError(
100
+ f"Context for run_id={run_id} doesn't exist."
101
+ " A run context must be registered before it can be retrieved or updated "
102
+ " by a client."
103
+ )
104
+
105
+ def update_context(self, run_id: int, context: Context) -> None:
106
+ """Update run context."""
107
+ if context.run_config != self.run_infos[run_id].initial_run_config:
108
+ raise ValueError(
109
+ "The `run_config` field of the `Context` object cannot be "
110
+ f"modified (run_id: {run_id})."
111
+ )
112
+ self.run_infos[run_id].context = context
@@ -0,0 +1,24 @@
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 SuperNode."""
16
+
17
+
18
+ from .app import run_client_app as run_client_app
19
+ from .app import run_supernode as run_supernode
20
+
21
+ __all__ = [
22
+ "run_client_app",
23
+ "run_supernode",
24
+ ]
@@ -0,0 +1,321 @@
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 SuperNode."""
16
+
17
+
18
+ import argparse
19
+ import sys
20
+ from logging import DEBUG, ERROR, INFO, WARN
21
+ from pathlib import Path
22
+ from typing import Optional
23
+
24
+ from cryptography.exceptions import UnsupportedAlgorithm
25
+ from cryptography.hazmat.primitives.asymmetric import ec
26
+ from cryptography.hazmat.primitives.serialization import (
27
+ load_ssh_private_key,
28
+ load_ssh_public_key,
29
+ )
30
+
31
+ from flwr.common import EventType, event
32
+ from flwr.common.args import try_obtain_root_certificates
33
+ from flwr.common.config import parse_config_args
34
+ from flwr.common.constant import (
35
+ CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
36
+ FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
37
+ ISOLATION_MODE_PROCESS,
38
+ ISOLATION_MODE_SUBPROCESS,
39
+ TRANSPORT_TYPE_GRPC_ADAPTER,
40
+ TRANSPORT_TYPE_GRPC_RERE,
41
+ TRANSPORT_TYPE_REST,
42
+ )
43
+ from flwr.common.exit_handlers import register_exit_handlers
44
+ from flwr.common.logger import log, warn_deprecated_feature
45
+
46
+ from ..app import start_client_internal
47
+ from ..clientapp.utils import get_load_client_app_fn
48
+
49
+
50
+ def run_supernode() -> None:
51
+ """Run Flower SuperNode."""
52
+ args = _parse_args_run_supernode().parse_args()
53
+ _warn_deprecated_server_arg(args)
54
+
55
+ log(INFO, "Starting Flower SuperNode")
56
+
57
+ event(EventType.RUN_SUPERNODE_ENTER)
58
+
59
+ # Check if both `--flwr-dir` and `--isolation` were set
60
+ if args.flwr_dir is not None and args.isolation is not None:
61
+ log(
62
+ WARN,
63
+ "Both `--flwr-dir` and `--isolation` were specified. "
64
+ "Ignoring `--flwr-dir`.",
65
+ )
66
+
67
+ # Exit if unsupported argument is passed by the user
68
+ if args.app is not None:
69
+ log(
70
+ ERROR,
71
+ "The `app` argument is deprecated. The SuperNode now automatically "
72
+ "uses the ClientApp delivered from the SuperLink. Providing the app "
73
+ "directory manually is no longer supported. Please remove the `app` "
74
+ "argument from your command.",
75
+ )
76
+ sys.exit(1)
77
+
78
+ root_certificates = try_obtain_root_certificates(args, args.superlink)
79
+ load_fn = get_load_client_app_fn(
80
+ default_app_ref="",
81
+ app_path=None,
82
+ flwr_dir=args.flwr_dir,
83
+ multi_app=True,
84
+ )
85
+ authentication_keys = _try_setup_client_authentication(args)
86
+
87
+ log(DEBUG, "Isolation mode: %s", args.isolation)
88
+
89
+ start_client_internal(
90
+ server_address=args.superlink,
91
+ load_client_app_fn=load_fn,
92
+ transport=args.transport,
93
+ root_certificates=root_certificates,
94
+ insecure=args.insecure,
95
+ authentication_keys=authentication_keys,
96
+ max_retries=args.max_retries,
97
+ max_wait_time=args.max_wait_time,
98
+ node_config=parse_config_args(
99
+ [args.node_config] if args.node_config else args.node_config
100
+ ),
101
+ flwr_path=args.flwr_dir,
102
+ isolation=args.isolation,
103
+ clientappio_api_address=args.clientappio_api_address,
104
+ )
105
+
106
+ # Graceful shutdown
107
+ register_exit_handlers(
108
+ event_type=EventType.RUN_SUPERNODE_LEAVE,
109
+ )
110
+
111
+
112
+ def run_client_app() -> None:
113
+ """Run Flower client app."""
114
+ event(EventType.RUN_CLIENT_APP_ENTER)
115
+ log(
116
+ ERROR,
117
+ "The command `flower-client-app` has been replaced by `flwr run`.",
118
+ )
119
+ register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
120
+
121
+
122
+ def _warn_deprecated_server_arg(args: argparse.Namespace) -> None:
123
+ """Warn about the deprecated argument `--server`."""
124
+ if args.server != FLEET_API_GRPC_RERE_DEFAULT_ADDRESS:
125
+ warn = "Passing flag --server is deprecated. Use --superlink instead."
126
+ warn_deprecated_feature(warn)
127
+
128
+ if args.superlink != FLEET_API_GRPC_RERE_DEFAULT_ADDRESS:
129
+ # if `--superlink` also passed, then
130
+ # warn user that this argument overrides what was passed with `--server`
131
+ log(
132
+ WARN,
133
+ "Both `--server` and `--superlink` were passed. "
134
+ "`--server` will be ignored. Connecting to the Superlink Fleet API "
135
+ "at %s.",
136
+ args.superlink,
137
+ )
138
+ else:
139
+ args.superlink = args.server
140
+
141
+
142
+ def _parse_args_run_supernode() -> argparse.ArgumentParser:
143
+ """Parse flower-supernode command line arguments."""
144
+ parser = argparse.ArgumentParser(
145
+ description="Start a Flower SuperNode",
146
+ )
147
+
148
+ parser.add_argument(
149
+ "app",
150
+ nargs="?",
151
+ default=None,
152
+ help=(
153
+ "(REMOVED) This argument is removed. The SuperNode now automatically "
154
+ "uses the ClientApp delivered from the SuperLink, so there is no need to "
155
+ "provide the app directory manually. This argument will be removed in a "
156
+ "future version."
157
+ ),
158
+ )
159
+ _parse_args_common(parser)
160
+ parser.add_argument(
161
+ "--flwr-dir",
162
+ default=None,
163
+ help="""The path containing installed Flower Apps.
164
+ The default directory is:
165
+
166
+ - `$FLWR_HOME/` if `$FLWR_HOME` is defined
167
+ - `$XDG_DATA_HOME/.flwr/` if `$XDG_DATA_HOME` is defined
168
+ - `$HOME/.flwr/` in all other cases
169
+ """,
170
+ )
171
+ parser.add_argument(
172
+ "--isolation",
173
+ default=ISOLATION_MODE_SUBPROCESS,
174
+ required=False,
175
+ choices=[
176
+ ISOLATION_MODE_SUBPROCESS,
177
+ ISOLATION_MODE_PROCESS,
178
+ ],
179
+ help="Isolation mode when running a `ClientApp` (`subprocess` by default, "
180
+ "possible values: `subprocess`, `process`). Use `subprocess` to configure "
181
+ "SuperNode to run a `ClientApp` in a subprocess. Use `process` to indicate "
182
+ "that a separate independent process gets created outside of SuperNode.",
183
+ )
184
+ parser.add_argument(
185
+ "--clientappio-api-address",
186
+ default=CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
187
+ help="ClientAppIo API (gRPC) server address (IPv4, IPv6, or a domain name). "
188
+ f"By default, it is set to {CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS}.",
189
+ )
190
+
191
+ return parser
192
+
193
+
194
+ def _parse_args_common(parser: argparse.ArgumentParser) -> None:
195
+ parser.add_argument(
196
+ "--insecure",
197
+ action="store_true",
198
+ help="Run the client without HTTPS. By default, the client runs with "
199
+ "HTTPS enabled. Use this flag only if you understand the risks.",
200
+ )
201
+ ex_group = parser.add_mutually_exclusive_group()
202
+ ex_group.add_argument(
203
+ "--grpc-rere",
204
+ action="store_const",
205
+ dest="transport",
206
+ const=TRANSPORT_TYPE_GRPC_RERE,
207
+ default=TRANSPORT_TYPE_GRPC_RERE,
208
+ help="Use grpc-rere as a transport layer for the client.",
209
+ )
210
+ ex_group.add_argument(
211
+ "--grpc-adapter",
212
+ action="store_const",
213
+ dest="transport",
214
+ const=TRANSPORT_TYPE_GRPC_ADAPTER,
215
+ help="Use grpc-adapter as a transport layer for the client.",
216
+ )
217
+ ex_group.add_argument(
218
+ "--rest",
219
+ action="store_const",
220
+ dest="transport",
221
+ const=TRANSPORT_TYPE_REST,
222
+ help="Use REST as a transport layer for the client.",
223
+ )
224
+ parser.add_argument(
225
+ "--root-certificates",
226
+ metavar="ROOT_CERT",
227
+ type=str,
228
+ help="Specifies the path to the PEM-encoded root certificate file for "
229
+ "establishing secure HTTPS connections.",
230
+ )
231
+ parser.add_argument(
232
+ "--server",
233
+ default=FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
234
+ help="Server address",
235
+ )
236
+ parser.add_argument(
237
+ "--superlink",
238
+ default=FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
239
+ help="SuperLink Fleet API (gRPC-rere) address (IPv4, IPv6, or a domain name)",
240
+ )
241
+ parser.add_argument(
242
+ "--max-retries",
243
+ type=int,
244
+ default=None,
245
+ help="The maximum number of times the client will try to reconnect to the"
246
+ "SuperLink before giving up in case of a connection error. By default,"
247
+ "it is set to None, meaning there is no limit to the number of tries.",
248
+ )
249
+ parser.add_argument(
250
+ "--max-wait-time",
251
+ type=float,
252
+ default=None,
253
+ help="The maximum duration before the client stops trying to"
254
+ "connect to the SuperLink in case of connection error. By default, it"
255
+ "is set to None, meaning there is no limit to the total time.",
256
+ )
257
+ parser.add_argument(
258
+ "--auth-supernode-private-key",
259
+ type=str,
260
+ help="The SuperNode's private key (as a path str) to enable authentication.",
261
+ )
262
+ parser.add_argument(
263
+ "--auth-supernode-public-key",
264
+ type=str,
265
+ help="The SuperNode's public key (as a path str) to enable authentication.",
266
+ )
267
+ parser.add_argument(
268
+ "--node-config",
269
+ type=str,
270
+ help="A space separated list of key/value pairs (separated by `=`) to "
271
+ "configure the SuperNode. "
272
+ "E.g. --node-config 'key1=\"value1\" partition-id=0 num-partitions=100'",
273
+ )
274
+
275
+
276
+ def _try_setup_client_authentication(
277
+ args: argparse.Namespace,
278
+ ) -> Optional[tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]]:
279
+ if not args.auth_supernode_private_key and not args.auth_supernode_public_key:
280
+ return None
281
+
282
+ if not args.auth_supernode_private_key or not args.auth_supernode_public_key:
283
+ sys.exit(
284
+ "Authentication requires file paths to both "
285
+ "'--auth-supernode-private-key' and '--auth-supernode-public-key'"
286
+ "to be provided (providing only one of them is not sufficient)."
287
+ )
288
+
289
+ try:
290
+ ssh_private_key = load_ssh_private_key(
291
+ Path(args.auth_supernode_private_key).read_bytes(),
292
+ None,
293
+ )
294
+ if not isinstance(ssh_private_key, ec.EllipticCurvePrivateKey):
295
+ raise ValueError()
296
+ except (ValueError, UnsupportedAlgorithm):
297
+ sys.exit(
298
+ "Error: Unable to parse the private key file in "
299
+ "'--auth-supernode-private-key'. Authentication requires elliptic "
300
+ "curve private and public key pair. Please ensure that the file "
301
+ "path points to a valid private key file and try again."
302
+ )
303
+
304
+ try:
305
+ ssh_public_key = load_ssh_public_key(
306
+ Path(args.auth_supernode_public_key).read_bytes()
307
+ )
308
+ if not isinstance(ssh_public_key, ec.EllipticCurvePublicKey):
309
+ raise ValueError()
310
+ except (ValueError, UnsupportedAlgorithm):
311
+ sys.exit(
312
+ "Error: Unable to parse the public key file in "
313
+ "'--auth-supernode-public-key'. Authentication requires elliptic "
314
+ "curve private and public key pair. Please ensure that the file "
315
+ "path points to a valid public key file and try again."
316
+ )
317
+
318
+ return (
319
+ ssh_private_key,
320
+ ssh_public_key,
321
+ )
flwr/client/typing.py CHANGED
@@ -23,6 +23,7 @@ from .client import Client as Client
23
23
 
24
24
  # Compatibility
25
25
  ClientFn = Callable[[str], Client]
26
+ ClientFnExt = Callable[[Context], Client]
26
27
 
27
28
  ClientAppCallable = Callable[[Message, Context], Message]
28
29
  Mod = Callable[[Message, Context, ClientAppCallable], Message]
flwr/common/__init__.py CHANGED
@@ -22,6 +22,7 @@ from .date import now as now
22
22
  from .grpc import GRPC_MAX_MESSAGE_LENGTH
23
23
  from .logger import configure as configure
24
24
  from .logger import log as log
25
+ from .message import DEFAULT_TTL
25
26
  from .message import Error as Error
26
27
  from .message import Message as Message
27
28
  from .message import Metadata as Metadata
@@ -40,6 +41,7 @@ from .telemetry import event as event
40
41
  from .typing import ClientMessage as ClientMessage
41
42
  from .typing import Code as Code
42
43
  from .typing import Config as Config
44
+ from .typing import ConfigsRecordValues as ConfigsRecordValues
43
45
  from .typing import DisconnectRes as DisconnectRes
44
46
  from .typing import EvaluateIns as EvaluateIns
45
47
  from .typing import EvaluateRes as EvaluateRes
@@ -51,6 +53,7 @@ from .typing import GetPropertiesIns as GetPropertiesIns
51
53
  from .typing import GetPropertiesRes as GetPropertiesRes
52
54
  from .typing import Metrics as Metrics
53
55
  from .typing import MetricsAggregationFn as MetricsAggregationFn
56
+ from .typing import MetricsRecordValues as MetricsRecordValues
54
57
  from .typing import NDArray as NDArray
55
58
  from .typing import NDArrays as NDArrays
56
59
  from .typing import Parameters as Parameters
@@ -62,28 +65,25 @@ from .typing import Status as Status
62
65
 
63
66
  __all__ = [
64
67
  "Array",
65
- "array_from_numpy",
66
- "bytes_to_ndarray",
67
68
  "ClientMessage",
68
69
  "Code",
69
70
  "Config",
70
71
  "ConfigsRecord",
71
- "configure",
72
+ "ConfigsRecordValues",
72
73
  "Context",
74
+ "DEFAULT_TTL",
73
75
  "DisconnectRes",
76
+ "Error",
74
77
  "EvaluateIns",
75
78
  "EvaluateRes",
76
- "event",
77
79
  "EventType",
78
80
  "FitIns",
79
81
  "FitRes",
80
- "Error",
82
+ "GRPC_MAX_MESSAGE_LENGTH",
81
83
  "GetParametersIns",
82
84
  "GetParametersRes",
83
85
  "GetPropertiesIns",
84
86
  "GetPropertiesRes",
85
- "GRPC_MAX_MESSAGE_LENGTH",
86
- "log",
87
87
  "Message",
88
88
  "MessageType",
89
89
  "MessageTypeLegacy",
@@ -91,13 +91,10 @@ __all__ = [
91
91
  "Metrics",
92
92
  "MetricsAggregationFn",
93
93
  "MetricsRecord",
94
- "ndarray_to_bytes",
95
- "now",
94
+ "MetricsRecordValues",
96
95
  "NDArray",
97
96
  "NDArrays",
98
- "ndarrays_to_parameters",
99
97
  "Parameters",
100
- "parameters_to_ndarrays",
101
98
  "ParametersRecord",
102
99
  "Properties",
103
100
  "ReconnectIns",
@@ -105,4 +102,13 @@ __all__ = [
105
102
  "Scalar",
106
103
  "ServerMessage",
107
104
  "Status",
105
+ "array_from_numpy",
106
+ "bytes_to_ndarray",
107
+ "configure",
108
+ "event",
109
+ "log",
110
+ "ndarray_to_bytes",
111
+ "ndarrays_to_parameters",
112
+ "now",
113
+ "parameters_to_ndarrays",
108
114
  ]
flwr/common/address.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -14,13 +14,15 @@
14
14
  # ==============================================================================
15
15
  """Flower IP address utils."""
16
16
 
17
+
18
+ import socket
17
19
  from ipaddress import ip_address
18
- from typing import Optional, Tuple
20
+ from typing import Optional
19
21
 
20
22
  IPV6: int = 6
21
23
 
22
24
 
23
- def parse_address(address: str) -> Optional[Tuple[str, int, Optional[bool]]]:
25
+ def parse_address(address: str) -> Optional[tuple[str, int, Optional[bool]]]:
24
26
  """Parse an IP address into host, port, and version.
25
27
 
26
28
  Parameters
@@ -57,3 +59,45 @@ def parse_address(address: str) -> Optional[Tuple[str, int, Optional[bool]]]:
57
59
 
58
60
  except ValueError:
59
61
  return None
62
+
63
+
64
+ def is_port_in_use(address: str) -> bool:
65
+ """Check if the port specified in address is in use.
66
+
67
+ Parameters
68
+ ----------
69
+ address : str
70
+ The string representation of a domain, an IPv4, or an IPV6 address
71
+ with the port number.
72
+
73
+ For example, '127.0.0.1:8080', or `[::1]:8080`.
74
+
75
+ Returns
76
+ -------
77
+ bool
78
+ If the port provided is in use or can't be parsed,
79
+ the function will return True, otherwise it will return False.
80
+ """
81
+ parsed_address = parse_address(address)
82
+ if not parsed_address:
83
+ return True
84
+ host, port, is_v6 = parsed_address
85
+
86
+ if is_v6:
87
+ protocol = socket.AF_INET6
88
+ else:
89
+ protocol = socket.AF_INET
90
+
91
+ with socket.socket(protocol, socket.SOCK_STREAM) as s:
92
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
93
+ try:
94
+ if is_v6:
95
+ # For IPv6, provide `flowinfo` and `scopeid` as 0
96
+ s.bind((host, port, 0, 0))
97
+ else:
98
+ # For IPv4
99
+ s.bind((host, port))
100
+ except OSError:
101
+ return True
102
+
103
+ return False