flwr-nightly 1.8.0.dev20240315__py3-none-any.whl → 1.11.0.dev20240813__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flwr-nightly might be problematic. Click here for more details.

Files changed (237) hide show
  1. flwr/cli/app.py +7 -0
  2. flwr/cli/build.py +150 -0
  3. flwr/cli/config_utils.py +219 -0
  4. flwr/cli/example.py +3 -1
  5. flwr/cli/install.py +227 -0
  6. flwr/cli/new/new.py +179 -48
  7. flwr/cli/new/templates/app/.gitignore.tpl +160 -0
  8. flwr/cli/new/templates/app/README.flowertune.md.tpl +56 -0
  9. flwr/cli/new/templates/app/README.md.tpl +1 -5
  10. flwr/cli/new/templates/app/code/__init__.py.tpl +1 -1
  11. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +65 -0
  12. flwr/cli/new/templates/app/code/client.jax.py.tpl +56 -0
  13. flwr/cli/new/templates/app/code/client.mlx.py.tpl +93 -0
  14. flwr/cli/new/templates/app/code/client.numpy.py.tpl +3 -2
  15. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +23 -11
  16. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +97 -0
  17. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +60 -1
  18. flwr/cli/new/templates/app/code/flwr_tune/__init__.py +15 -0
  19. flwr/cli/new/templates/app/code/flwr_tune/app.py.tpl +89 -0
  20. flwr/cli/new/templates/app/code/flwr_tune/client.py.tpl +126 -0
  21. flwr/cli/new/templates/app/code/flwr_tune/config.yaml.tpl +34 -0
  22. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +57 -0
  23. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +59 -0
  24. flwr/cli/new/templates/app/code/flwr_tune/server.py.tpl +48 -0
  25. flwr/cli/new/templates/app/code/flwr_tune/static_config.yaml.tpl +11 -0
  26. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +23 -0
  27. flwr/cli/new/templates/app/code/server.jax.py.tpl +20 -0
  28. flwr/cli/new/templates/app/code/server.mlx.py.tpl +20 -0
  29. flwr/cli/new/templates/app/code/server.numpy.py.tpl +17 -9
  30. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +21 -18
  31. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +24 -0
  32. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +29 -1
  33. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +99 -0
  34. flwr/cli/new/templates/app/code/task.jax.py.tpl +57 -0
  35. flwr/cli/new/templates/app/code/task.mlx.py.tpl +102 -0
  36. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +28 -23
  37. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +53 -0
  38. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +39 -0
  39. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +38 -0
  40. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +34 -0
  41. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +39 -0
  42. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +25 -12
  43. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +29 -14
  44. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +33 -0
  45. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +29 -14
  46. flwr/cli/run/run.py +168 -17
  47. flwr/cli/utils.py +75 -4
  48. flwr/client/__init__.py +6 -1
  49. flwr/client/app.py +239 -248
  50. flwr/client/client_app.py +70 -9
  51. flwr/client/dpfedavg_numpy_client.py +1 -1
  52. flwr/client/grpc_adapter_client/__init__.py +15 -0
  53. flwr/client/grpc_adapter_client/connection.py +97 -0
  54. flwr/client/grpc_client/connection.py +18 -5
  55. flwr/client/grpc_rere_client/__init__.py +1 -1
  56. flwr/client/grpc_rere_client/client_interceptor.py +158 -0
  57. flwr/client/grpc_rere_client/connection.py +127 -33
  58. flwr/client/grpc_rere_client/grpc_adapter.py +140 -0
  59. flwr/client/heartbeat.py +74 -0
  60. flwr/client/message_handler/__init__.py +1 -1
  61. flwr/client/message_handler/message_handler.py +7 -7
  62. flwr/client/mod/__init__.py +5 -5
  63. flwr/client/mod/centraldp_mods.py +4 -2
  64. flwr/client/mod/comms_mods.py +4 -4
  65. flwr/client/mod/localdp_mod.py +9 -4
  66. flwr/client/mod/secure_aggregation/__init__.py +1 -1
  67. flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
  68. flwr/client/mod/utils.py +1 -1
  69. flwr/client/node_state.py +60 -10
  70. flwr/client/node_state_tests.py +4 -3
  71. flwr/client/rest_client/__init__.py +1 -1
  72. flwr/client/rest_client/connection.py +177 -157
  73. flwr/client/supernode/__init__.py +26 -0
  74. flwr/client/supernode/app.py +464 -0
  75. flwr/client/typing.py +1 -0
  76. flwr/common/__init__.py +13 -11
  77. flwr/common/address.py +1 -1
  78. flwr/common/config.py +193 -0
  79. flwr/common/constant.py +42 -1
  80. flwr/common/context.py +26 -1
  81. flwr/common/date.py +1 -1
  82. flwr/common/dp.py +1 -1
  83. flwr/common/grpc.py +6 -2
  84. flwr/common/logger.py +79 -8
  85. flwr/common/message.py +167 -105
  86. flwr/common/object_ref.py +126 -25
  87. flwr/common/record/__init__.py +1 -1
  88. flwr/common/record/parametersrecord.py +0 -1
  89. flwr/common/record/recordset.py +78 -27
  90. flwr/common/recordset_compat.py +8 -1
  91. flwr/common/retry_invoker.py +25 -13
  92. flwr/common/secure_aggregation/__init__.py +1 -1
  93. flwr/common/secure_aggregation/crypto/__init__.py +1 -1
  94. flwr/common/secure_aggregation/crypto/shamir.py +1 -1
  95. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +21 -2
  96. flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
  97. flwr/common/secure_aggregation/quantization.py +1 -1
  98. flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
  99. flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
  100. flwr/common/serde.py +209 -3
  101. flwr/common/telemetry.py +25 -0
  102. flwr/common/typing.py +38 -0
  103. flwr/common/version.py +14 -0
  104. flwr/proto/clientappio_pb2.py +41 -0
  105. flwr/proto/clientappio_pb2.pyi +110 -0
  106. flwr/proto/clientappio_pb2_grpc.py +101 -0
  107. flwr/proto/clientappio_pb2_grpc.pyi +40 -0
  108. flwr/proto/common_pb2.py +36 -0
  109. flwr/proto/common_pb2.pyi +121 -0
  110. flwr/proto/common_pb2_grpc.py +4 -0
  111. flwr/proto/common_pb2_grpc.pyi +4 -0
  112. flwr/proto/driver_pb2.py +26 -19
  113. flwr/proto/driver_pb2.pyi +34 -0
  114. flwr/proto/driver_pb2_grpc.py +70 -0
  115. flwr/proto/driver_pb2_grpc.pyi +28 -0
  116. flwr/proto/exec_pb2.py +43 -0
  117. flwr/proto/exec_pb2.pyi +95 -0
  118. flwr/proto/exec_pb2_grpc.py +101 -0
  119. flwr/proto/exec_pb2_grpc.pyi +41 -0
  120. flwr/proto/fab_pb2.py +30 -0
  121. flwr/proto/fab_pb2.pyi +56 -0
  122. flwr/proto/fab_pb2_grpc.py +4 -0
  123. flwr/proto/fab_pb2_grpc.pyi +4 -0
  124. flwr/proto/fleet_pb2.py +29 -23
  125. flwr/proto/fleet_pb2.pyi +33 -0
  126. flwr/proto/fleet_pb2_grpc.py +102 -0
  127. flwr/proto/fleet_pb2_grpc.pyi +35 -0
  128. flwr/proto/grpcadapter_pb2.py +32 -0
  129. flwr/proto/grpcadapter_pb2.pyi +43 -0
  130. flwr/proto/grpcadapter_pb2_grpc.py +66 -0
  131. flwr/proto/grpcadapter_pb2_grpc.pyi +24 -0
  132. flwr/proto/message_pb2.py +41 -0
  133. flwr/proto/message_pb2.pyi +122 -0
  134. flwr/proto/message_pb2_grpc.py +4 -0
  135. flwr/proto/message_pb2_grpc.pyi +4 -0
  136. flwr/proto/run_pb2.py +35 -0
  137. flwr/proto/run_pb2.pyi +76 -0
  138. flwr/proto/run_pb2_grpc.py +4 -0
  139. flwr/proto/run_pb2_grpc.pyi +4 -0
  140. flwr/proto/task_pb2.py +7 -8
  141. flwr/proto/task_pb2.pyi +8 -5
  142. flwr/server/__init__.py +4 -8
  143. flwr/server/app.py +298 -350
  144. flwr/server/compat/app.py +6 -57
  145. flwr/server/compat/app_utils.py +5 -4
  146. flwr/server/compat/driver_client_proxy.py +29 -48
  147. flwr/server/compat/legacy_context.py +5 -4
  148. flwr/server/driver/__init__.py +2 -0
  149. flwr/server/driver/driver.py +22 -132
  150. flwr/server/driver/grpc_driver.py +224 -74
  151. flwr/server/driver/inmemory_driver.py +183 -0
  152. flwr/server/history.py +20 -20
  153. flwr/server/run_serverapp.py +121 -34
  154. flwr/server/server.py +11 -7
  155. flwr/server/server_app.py +59 -10
  156. flwr/server/serverapp_components.py +52 -0
  157. flwr/server/strategy/__init__.py +2 -2
  158. flwr/server/strategy/bulyan.py +1 -1
  159. flwr/server/strategy/dp_adaptive_clipping.py +3 -3
  160. flwr/server/strategy/dp_fixed_clipping.py +4 -3
  161. flwr/server/strategy/dpfedavg_adaptive.py +1 -1
  162. flwr/server/strategy/dpfedavg_fixed.py +1 -1
  163. flwr/server/strategy/fedadagrad.py +1 -1
  164. flwr/server/strategy/fedadam.py +1 -1
  165. flwr/server/strategy/fedavg_android.py +1 -1
  166. flwr/server/strategy/fedavgm.py +1 -1
  167. flwr/server/strategy/fedmedian.py +1 -1
  168. flwr/server/strategy/fedopt.py +1 -1
  169. flwr/server/strategy/fedprox.py +1 -1
  170. flwr/server/strategy/fedxgb_bagging.py +1 -1
  171. flwr/server/strategy/fedxgb_cyclic.py +1 -1
  172. flwr/server/strategy/fedxgb_nn_avg.py +1 -1
  173. flwr/server/strategy/fedyogi.py +1 -1
  174. flwr/server/strategy/krum.py +1 -1
  175. flwr/server/strategy/qfedavg.py +1 -1
  176. flwr/server/superlink/driver/__init__.py +1 -1
  177. flwr/server/superlink/driver/driver_grpc.py +1 -1
  178. flwr/server/superlink/driver/driver_servicer.py +51 -4
  179. flwr/server/superlink/ffs/__init__.py +24 -0
  180. flwr/server/superlink/ffs/disk_ffs.py +104 -0
  181. flwr/server/superlink/ffs/ffs.py +79 -0
  182. flwr/server/superlink/fleet/__init__.py +1 -1
  183. flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
  184. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +131 -0
  185. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  186. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
  187. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
  188. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  189. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +8 -2
  190. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  191. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +30 -2
  192. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +214 -0
  193. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  194. flwr/server/superlink/fleet/message_handler/message_handler.py +42 -2
  195. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  196. flwr/server/superlink/fleet/rest_rere/rest_api.py +59 -1
  197. flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
  198. flwr/server/superlink/fleet/vce/backend/backend.py +5 -5
  199. flwr/server/superlink/fleet/vce/backend/raybackend.py +53 -56
  200. flwr/server/superlink/fleet/vce/vce_api.py +190 -127
  201. flwr/server/superlink/state/__init__.py +1 -1
  202. flwr/server/superlink/state/in_memory_state.py +159 -42
  203. flwr/server/superlink/state/sqlite_state.py +243 -39
  204. flwr/server/superlink/state/state.py +81 -6
  205. flwr/server/superlink/state/state_factory.py +11 -2
  206. flwr/server/superlink/state/utils.py +62 -0
  207. flwr/server/typing.py +2 -0
  208. flwr/server/utils/__init__.py +1 -1
  209. flwr/server/utils/tensorboard.py +1 -1
  210. flwr/server/utils/validator.py +23 -9
  211. flwr/server/workflow/default_workflows.py +67 -25
  212. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +18 -6
  213. flwr/simulation/__init__.py +7 -4
  214. flwr/simulation/app.py +67 -36
  215. flwr/simulation/ray_transport/__init__.py +1 -1
  216. flwr/simulation/ray_transport/ray_actor.py +20 -46
  217. flwr/simulation/ray_transport/ray_client_proxy.py +36 -16
  218. flwr/simulation/run_simulation.py +308 -92
  219. flwr/superexec/__init__.py +21 -0
  220. flwr/superexec/app.py +184 -0
  221. flwr/superexec/deployment.py +185 -0
  222. flwr/superexec/exec_grpc.py +55 -0
  223. flwr/superexec/exec_servicer.py +70 -0
  224. flwr/superexec/executor.py +75 -0
  225. flwr/superexec/simulation.py +193 -0
  226. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/METADATA +10 -6
  227. flwr_nightly-1.11.0.dev20240813.dist-info/RECORD +288 -0
  228. flwr_nightly-1.11.0.dev20240813.dist-info/entry_points.txt +10 -0
  229. flwr/cli/flower_toml.py +0 -140
  230. flwr/cli/new/templates/app/flower.toml.tpl +0 -13
  231. flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -2
  232. flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +0 -4
  233. flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -4
  234. flwr_nightly-1.8.0.dev20240315.dist-info/RECORD +0 -211
  235. flwr_nightly-1.8.0.dev20240315.dist-info/entry_points.txt +0 -9
  236. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/LICENSE +0 -0
  237. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/WHEEL +0 -0
@@ -0,0 +1,464 @@
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
+ import argparse
18
+ import sys
19
+ from logging import DEBUG, INFO, WARN
20
+ from pathlib import Path
21
+ from typing import Callable, Optional, Tuple
22
+
23
+ from cryptography.exceptions import UnsupportedAlgorithm
24
+ from cryptography.hazmat.primitives.asymmetric import ec
25
+ from cryptography.hazmat.primitives.serialization import (
26
+ load_ssh_private_key,
27
+ load_ssh_public_key,
28
+ )
29
+
30
+ from flwr.client.client_app import ClientApp, LoadClientAppError
31
+ from flwr.common import EventType, event
32
+ from flwr.common.config import (
33
+ get_flwr_dir,
34
+ get_metadata_from_config,
35
+ get_project_config,
36
+ get_project_dir,
37
+ parse_config_args,
38
+ )
39
+ from flwr.common.constant import (
40
+ TRANSPORT_TYPE_GRPC_ADAPTER,
41
+ TRANSPORT_TYPE_GRPC_RERE,
42
+ TRANSPORT_TYPE_REST,
43
+ )
44
+ from flwr.common.exit_handlers import register_exit_handlers
45
+ from flwr.common.logger import log, warn_deprecated_feature
46
+ from flwr.common.object_ref import load_app, validate
47
+
48
+ from ..app import _start_client_internal
49
+
50
+ ADDRESS_FLEET_API_GRPC_RERE = "0.0.0.0:9092"
51
+
52
+
53
+ def run_supernode() -> None:
54
+ """Run Flower SuperNode."""
55
+ log(INFO, "Starting Flower SuperNode")
56
+
57
+ event(EventType.RUN_SUPERNODE_ENTER)
58
+
59
+ args = _parse_args_run_supernode().parse_args()
60
+
61
+ _warn_deprecated_server_arg(args)
62
+
63
+ root_certificates = _get_certificates(args)
64
+ load_fn = _get_load_client_app_fn(
65
+ default_app_ref="",
66
+ app_path=args.app,
67
+ flwr_dir=args.flwr_dir,
68
+ multi_app=True,
69
+ )
70
+ authentication_keys = _try_setup_client_authentication(args)
71
+
72
+ _start_client_internal(
73
+ server_address=args.superlink,
74
+ load_client_app_fn=load_fn,
75
+ transport=args.transport,
76
+ root_certificates=root_certificates,
77
+ insecure=args.insecure,
78
+ authentication_keys=authentication_keys,
79
+ max_retries=args.max_retries,
80
+ max_wait_time=args.max_wait_time,
81
+ node_config=parse_config_args([args.node_config]),
82
+ flwr_path=get_flwr_dir(args.flwr_dir),
83
+ )
84
+
85
+ # Graceful shutdown
86
+ register_exit_handlers(
87
+ event_type=EventType.RUN_SUPERNODE_LEAVE,
88
+ )
89
+
90
+
91
+ def run_client_app() -> None:
92
+ """Run Flower client app."""
93
+ log(INFO, "Long-running Flower client starting")
94
+
95
+ event(EventType.RUN_CLIENT_APP_ENTER)
96
+
97
+ args = _parse_args_run_client_app().parse_args()
98
+
99
+ _warn_deprecated_server_arg(args)
100
+
101
+ root_certificates = _get_certificates(args)
102
+ load_fn = _get_load_client_app_fn(
103
+ default_app_ref=getattr(args, "client-app"),
104
+ app_path=args.dir,
105
+ multi_app=False,
106
+ )
107
+ authentication_keys = _try_setup_client_authentication(args)
108
+
109
+ _start_client_internal(
110
+ server_address=args.superlink,
111
+ node_config=parse_config_args([args.node_config]),
112
+ load_client_app_fn=load_fn,
113
+ transport=args.transport,
114
+ root_certificates=root_certificates,
115
+ insecure=args.insecure,
116
+ authentication_keys=authentication_keys,
117
+ max_retries=args.max_retries,
118
+ max_wait_time=args.max_wait_time,
119
+ )
120
+ register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
121
+
122
+
123
+ def flwr_clientapp() -> None:
124
+ """Run process-isolated Flower ClientApp."""
125
+ log(INFO, "Starting Flower ClientApp")
126
+
127
+ parser = argparse.ArgumentParser(
128
+ description="Run a Flower ClientApp",
129
+ )
130
+ parser.add_argument(
131
+ "--address",
132
+ help="Address of SuperNode ClientAppIo gRPC servicer",
133
+ )
134
+ parser.add_argument(
135
+ "--token",
136
+ help="Unique token generated by SuperNode for each ClientApp execution",
137
+ )
138
+ args = parser.parse_args()
139
+ log(
140
+ DEBUG,
141
+ "Staring isolated `ClientApp` connected to SuperNode ClientAppIo at %s "
142
+ "with the token %s",
143
+ args.address,
144
+ args.token,
145
+ )
146
+
147
+
148
+ def _warn_deprecated_server_arg(args: argparse.Namespace) -> None:
149
+ """Warn about the deprecated argument `--server`."""
150
+ if args.server != ADDRESS_FLEET_API_GRPC_RERE:
151
+ warn = "Passing flag --server is deprecated. Use --superlink instead."
152
+ warn_deprecated_feature(warn)
153
+
154
+ if args.superlink != ADDRESS_FLEET_API_GRPC_RERE:
155
+ # if `--superlink` also passed, then
156
+ # warn user that this argument overrides what was passed with `--server`
157
+ log(
158
+ WARN,
159
+ "Both `--server` and `--superlink` were passed. "
160
+ "`--server` will be ignored. Connecting to the Superlink Fleet API "
161
+ "at %s.",
162
+ args.superlink,
163
+ )
164
+ else:
165
+ args.superlink = args.server
166
+
167
+
168
+ def _get_certificates(args: argparse.Namespace) -> Optional[bytes]:
169
+ """Load certificates if specified in args."""
170
+ # Obtain certificates
171
+ if args.insecure:
172
+ if args.root_certificates is not None:
173
+ sys.exit(
174
+ "Conflicting options: The '--insecure' flag disables HTTPS, "
175
+ "but '--root-certificates' was also specified. Please remove "
176
+ "the '--root-certificates' option when running in insecure mode, "
177
+ "or omit '--insecure' to use HTTPS."
178
+ )
179
+ log(
180
+ WARN,
181
+ "Option `--insecure` was set. "
182
+ "Starting insecure HTTP client connected to %s.",
183
+ args.superlink,
184
+ )
185
+ root_certificates = None
186
+ else:
187
+ # Load the certificates if provided, or load the system certificates
188
+ cert_path = args.root_certificates
189
+ if cert_path is None:
190
+ root_certificates = None
191
+ else:
192
+ root_certificates = Path(cert_path).read_bytes()
193
+ log(
194
+ DEBUG,
195
+ "Starting secure HTTPS client connected to %s "
196
+ "with the following certificates: %s.",
197
+ args.superlink,
198
+ cert_path,
199
+ )
200
+ return root_certificates
201
+
202
+
203
+ def _get_load_client_app_fn(
204
+ default_app_ref: str,
205
+ app_path: Optional[str],
206
+ multi_app: bool,
207
+ flwr_dir: Optional[str] = None,
208
+ ) -> Callable[[str, str], ClientApp]:
209
+ """Get the load_client_app_fn function.
210
+
211
+ If `multi_app` is True, this function loads the specified ClientApp
212
+ based on `fab_id` and `fab_version`. If `fab_id` is empty, a default
213
+ ClientApp will be loaded.
214
+
215
+ If `multi_app` is False, it ignores `fab_id` and `fab_version` and
216
+ loads a default ClientApp.
217
+ """
218
+ if not multi_app:
219
+ log(
220
+ DEBUG,
221
+ "Flower SuperNode will load and validate ClientApp `%s`",
222
+ default_app_ref,
223
+ )
224
+
225
+ valid, error_msg = validate(default_app_ref, project_dir=app_path)
226
+ if not valid and error_msg:
227
+ raise LoadClientAppError(error_msg) from None
228
+
229
+ def _load(fab_id: str, fab_version: str) -> ClientApp:
230
+ runtime_app_dir = Path(app_path if app_path else "").absolute()
231
+ # If multi-app feature is disabled
232
+ if not multi_app:
233
+ # Set app reference
234
+ client_app_ref = default_app_ref
235
+ # If multi-app feature is enabled but app directory is provided
236
+ elif app_path is not None:
237
+ config = get_project_config(runtime_app_dir)
238
+ this_fab_version, this_fab_id = get_metadata_from_config(config)
239
+
240
+ if this_fab_version != fab_version or this_fab_id != fab_id:
241
+ raise LoadClientAppError(
242
+ f"FAB ID or version mismatch: Expected FAB ID '{this_fab_id}' and "
243
+ f"FAB version '{this_fab_version}', but received FAB ID '{fab_id}' "
244
+ f"and FAB version '{fab_version}'.",
245
+ ) from None
246
+
247
+ # log(WARN, "FAB ID is not provided; the default ClientApp will be loaded.")
248
+
249
+ # Set app reference
250
+ client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
251
+ # If multi-app feature is enabled
252
+ else:
253
+ try:
254
+ runtime_app_dir = get_project_dir(
255
+ fab_id, fab_version, get_flwr_dir(flwr_dir)
256
+ )
257
+ config = get_project_config(runtime_app_dir)
258
+ except Exception as e:
259
+ raise LoadClientAppError("Failed to load ClientApp") from e
260
+
261
+ # Set app reference
262
+ client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
263
+
264
+ # Load ClientApp
265
+ log(
266
+ DEBUG,
267
+ "Loading ClientApp `%s`",
268
+ client_app_ref,
269
+ )
270
+ client_app = load_app(client_app_ref, LoadClientAppError, runtime_app_dir)
271
+
272
+ if not isinstance(client_app, ClientApp):
273
+ raise LoadClientAppError(
274
+ f"Attribute {client_app_ref} is not of type {ClientApp}",
275
+ ) from None
276
+
277
+ return client_app
278
+
279
+ return _load
280
+
281
+
282
+ def _parse_args_run_supernode() -> argparse.ArgumentParser:
283
+ """Parse flower-supernode command line arguments."""
284
+ parser = argparse.ArgumentParser(
285
+ description="Start a Flower SuperNode",
286
+ )
287
+
288
+ parser.add_argument(
289
+ "app",
290
+ nargs="?",
291
+ default=None,
292
+ help="Specify the path of the Flower App to load and run the `ClientApp`. "
293
+ "The `pyproject.toml` file must be located in the root of this path. "
294
+ "When this argument is provided, the SuperNode will exclusively respond to "
295
+ "messages from the corresponding `ServerApp` by matching the FAB ID and FAB "
296
+ "version. An error will be raised if a message is received from any other "
297
+ "`ServerApp`.",
298
+ )
299
+ _parse_args_common(parser)
300
+ parser.add_argument(
301
+ "--flwr-dir",
302
+ default=None,
303
+ help="""The path containing installed Flower Apps.
304
+ By default, this value is equal to:
305
+
306
+ - `$FLWR_HOME/` if `$FLWR_HOME` is defined
307
+ - `$XDG_DATA_HOME/.flwr/` if `$XDG_DATA_HOME` is defined
308
+ - `$HOME/.flwr/` in all other cases
309
+ """,
310
+ )
311
+
312
+ return parser
313
+
314
+
315
+ def _parse_args_run_client_app() -> argparse.ArgumentParser:
316
+ """Parse flower-client-app command line arguments."""
317
+ parser = argparse.ArgumentParser(
318
+ description="Start a Flower client app",
319
+ )
320
+
321
+ parser.add_argument(
322
+ "client-app",
323
+ help="For example: `client:app` or `project.package.module:wrapper.app`",
324
+ )
325
+ _parse_args_common(parser=parser)
326
+ parser.add_argument(
327
+ "--dir",
328
+ default="",
329
+ help="Add specified directory to the PYTHONPATH and load Flower "
330
+ "app from there."
331
+ " Default: current working directory.",
332
+ )
333
+
334
+ return parser
335
+
336
+
337
+ def _parse_args_common(parser: argparse.ArgumentParser) -> None:
338
+ parser.add_argument(
339
+ "--insecure",
340
+ action="store_true",
341
+ help="Run the client without HTTPS. By default, the client runs with "
342
+ "HTTPS enabled. Use this flag only if you understand the risks.",
343
+ )
344
+ ex_group = parser.add_mutually_exclusive_group()
345
+ ex_group.add_argument(
346
+ "--grpc-rere",
347
+ action="store_const",
348
+ dest="transport",
349
+ const=TRANSPORT_TYPE_GRPC_RERE,
350
+ default=TRANSPORT_TYPE_GRPC_RERE,
351
+ help="Use grpc-rere as a transport layer for the client.",
352
+ )
353
+ ex_group.add_argument(
354
+ "--grpc-adapter",
355
+ action="store_const",
356
+ dest="transport",
357
+ const=TRANSPORT_TYPE_GRPC_ADAPTER,
358
+ help="Use grpc-adapter as a transport layer for the client.",
359
+ )
360
+ ex_group.add_argument(
361
+ "--rest",
362
+ action="store_const",
363
+ dest="transport",
364
+ const=TRANSPORT_TYPE_REST,
365
+ help="Use REST as a transport layer for the client.",
366
+ )
367
+ parser.add_argument(
368
+ "--root-certificates",
369
+ metavar="ROOT_CERT",
370
+ type=str,
371
+ help="Specifies the path to the PEM-encoded root certificate file for "
372
+ "establishing secure HTTPS connections.",
373
+ )
374
+ parser.add_argument(
375
+ "--server",
376
+ default=ADDRESS_FLEET_API_GRPC_RERE,
377
+ help="Server address",
378
+ )
379
+ parser.add_argument(
380
+ "--superlink",
381
+ default=ADDRESS_FLEET_API_GRPC_RERE,
382
+ help="SuperLink Fleet API (gRPC-rere) address (IPv4, IPv6, or a domain name)",
383
+ )
384
+ parser.add_argument(
385
+ "--max-retries",
386
+ type=int,
387
+ default=None,
388
+ help="The maximum number of times the client will try to reconnect to the"
389
+ "SuperLink before giving up in case of a connection error. By default,"
390
+ "it is set to None, meaning there is no limit to the number of tries.",
391
+ )
392
+ parser.add_argument(
393
+ "--max-wait-time",
394
+ type=float,
395
+ default=None,
396
+ help="The maximum duration before the client stops trying to"
397
+ "connect to the SuperLink in case of connection error. By default, it"
398
+ "is set to None, meaning there is no limit to the total time.",
399
+ )
400
+ parser.add_argument(
401
+ "--auth-supernode-private-key",
402
+ type=str,
403
+ help="The SuperNode's private key (as a path str) to enable authentication.",
404
+ )
405
+ parser.add_argument(
406
+ "--auth-supernode-public-key",
407
+ type=str,
408
+ help="The SuperNode's public key (as a path str) to enable authentication.",
409
+ )
410
+ parser.add_argument(
411
+ "--node-config",
412
+ type=str,
413
+ help="A comma separated list of key/value pairs (separated by `=`) to "
414
+ "configure the SuperNode. "
415
+ "E.g. --node-config 'key1=\"value1\",partition-id=0,num-partitions=100'",
416
+ )
417
+
418
+
419
+ def _try_setup_client_authentication(
420
+ args: argparse.Namespace,
421
+ ) -> Optional[Tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]]:
422
+ if not args.auth_supernode_private_key and not args.auth_supernode_public_key:
423
+ return None
424
+
425
+ if not args.auth_supernode_private_key or not args.auth_supernode_public_key:
426
+ sys.exit(
427
+ "Authentication requires file paths to both "
428
+ "'--auth-supernode-private-key' and '--auth-supernode-public-key'"
429
+ "to be provided (providing only one of them is not sufficient)."
430
+ )
431
+
432
+ try:
433
+ ssh_private_key = load_ssh_private_key(
434
+ Path(args.auth_supernode_private_key).read_bytes(),
435
+ None,
436
+ )
437
+ if not isinstance(ssh_private_key, ec.EllipticCurvePrivateKey):
438
+ raise ValueError()
439
+ except (ValueError, UnsupportedAlgorithm):
440
+ sys.exit(
441
+ "Error: Unable to parse the private key file in "
442
+ "'--auth-supernode-private-key'. Authentication requires elliptic "
443
+ "curve private and public key pair. Please ensure that the file "
444
+ "path points to a valid private key file and try again."
445
+ )
446
+
447
+ try:
448
+ ssh_public_key = load_ssh_public_key(
449
+ Path(args.auth_supernode_public_key).read_bytes()
450
+ )
451
+ if not isinstance(ssh_public_key, ec.EllipticCurvePublicKey):
452
+ raise ValueError()
453
+ except (ValueError, UnsupportedAlgorithm):
454
+ sys.exit(
455
+ "Error: Unable to parse the public key file in "
456
+ "'--auth-supernode-public-key'. Authentication requires elliptic "
457
+ "curve private and public key pair. Please ensure that the file "
458
+ "path points to a valid public key file and try again."
459
+ )
460
+
461
+ return (
462
+ ssh_private_key,
463
+ ssh_public_key,
464
+ )
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
@@ -62,28 +63,24 @@ from .typing import Status as Status
62
63
 
63
64
  __all__ = [
64
65
  "Array",
65
- "array_from_numpy",
66
- "bytes_to_ndarray",
67
66
  "ClientMessage",
68
67
  "Code",
69
68
  "Config",
70
69
  "ConfigsRecord",
71
- "configure",
72
70
  "Context",
71
+ "DEFAULT_TTL",
73
72
  "DisconnectRes",
73
+ "Error",
74
74
  "EvaluateIns",
75
75
  "EvaluateRes",
76
- "event",
77
76
  "EventType",
78
77
  "FitIns",
79
78
  "FitRes",
80
- "Error",
79
+ "GRPC_MAX_MESSAGE_LENGTH",
81
80
  "GetParametersIns",
82
81
  "GetParametersRes",
83
82
  "GetPropertiesIns",
84
83
  "GetPropertiesRes",
85
- "GRPC_MAX_MESSAGE_LENGTH",
86
- "log",
87
84
  "Message",
88
85
  "MessageType",
89
86
  "MessageTypeLegacy",
@@ -91,13 +88,9 @@ __all__ = [
91
88
  "Metrics",
92
89
  "MetricsAggregationFn",
93
90
  "MetricsRecord",
94
- "ndarray_to_bytes",
95
- "now",
96
91
  "NDArray",
97
92
  "NDArrays",
98
- "ndarrays_to_parameters",
99
93
  "Parameters",
100
- "parameters_to_ndarrays",
101
94
  "ParametersRecord",
102
95
  "Properties",
103
96
  "ReconnectIns",
@@ -105,4 +98,13 @@ __all__ = [
105
98
  "Scalar",
106
99
  "ServerMessage",
107
100
  "Status",
101
+ "array_from_numpy",
102
+ "bytes_to_ndarray",
103
+ "configure",
104
+ "event",
105
+ "log",
106
+ "ndarray_to_bytes",
107
+ "ndarrays_to_parameters",
108
+ "now",
109
+ "parameters_to_ndarrays",
108
110
  ]
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.