flwr 1.23.0__py3-none-any.whl → 1.25.0__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.
Files changed (339) hide show
  1. flwr/__init__.py +16 -5
  2. flwr/app/error.py +2 -2
  3. flwr/app/exception.py +3 -3
  4. flwr/cli/app.py +19 -0
  5. flwr/cli/{new/templates → app_cmd}/__init__.py +9 -1
  6. flwr/cli/app_cmd/publish.py +285 -0
  7. flwr/cli/app_cmd/review.py +262 -0
  8. flwr/cli/auth_plugin/auth_plugin.py +4 -5
  9. flwr/cli/auth_plugin/noop_auth_plugin.py +54 -11
  10. flwr/cli/auth_plugin/oidc_cli_plugin.py +32 -9
  11. flwr/cli/build.py +60 -18
  12. flwr/cli/cli_account_auth_interceptor.py +24 -7
  13. flwr/cli/config_utils.py +101 -13
  14. flwr/cli/{new/templates/app/code/flwr_tune → federation}/__init__.py +10 -1
  15. flwr/cli/federation/ls.py +140 -0
  16. flwr/cli/federation/show.py +318 -0
  17. flwr/cli/install.py +91 -13
  18. flwr/cli/log.py +52 -9
  19. flwr/cli/login/login.py +7 -4
  20. flwr/cli/ls.py +211 -130
  21. flwr/cli/new/new.py +123 -331
  22. flwr/cli/pull.py +10 -5
  23. flwr/cli/run/run.py +71 -29
  24. flwr/cli/run_utils.py +148 -0
  25. flwr/cli/stop.py +26 -8
  26. flwr/cli/supernode/ls.py +25 -12
  27. flwr/cli/supernode/register.py +9 -4
  28. flwr/cli/supernode/unregister.py +5 -3
  29. flwr/cli/utils.py +239 -16
  30. flwr/client/__init__.py +1 -1
  31. flwr/client/dpfedavg_numpy_client.py +4 -1
  32. flwr/client/grpc_adapter_client/connection.py +8 -9
  33. flwr/client/grpc_rere_client/connection.py +16 -14
  34. flwr/client/grpc_rere_client/grpc_adapter.py +6 -2
  35. flwr/client/grpc_rere_client/node_auth_client_interceptor.py +2 -1
  36. flwr/client/message_handler/message_handler.py +2 -2
  37. flwr/client/mod/secure_aggregation/secaggplus_mod.py +3 -3
  38. flwr/client/numpy_client.py +1 -1
  39. flwr/client/rest_client/connection.py +18 -18
  40. flwr/client/run_info_store.py +4 -5
  41. flwr/client/typing.py +1 -1
  42. flwr/clientapp/client_app.py +9 -10
  43. flwr/clientapp/mod/centraldp_mods.py +16 -17
  44. flwr/clientapp/mod/localdp_mod.py +8 -9
  45. flwr/clientapp/typing.py +1 -1
  46. flwr/clientapp/utils.py +3 -3
  47. flwr/common/address.py +1 -2
  48. flwr/common/args.py +3 -4
  49. flwr/common/config.py +13 -16
  50. flwr/common/constant.py +5 -2
  51. flwr/common/differential_privacy.py +3 -4
  52. flwr/common/event_log_plugin/event_log_plugin.py +3 -4
  53. flwr/common/exit/exit.py +15 -2
  54. flwr/common/exit/exit_code.py +19 -0
  55. flwr/common/exit/exit_handler.py +6 -2
  56. flwr/common/exit/signal_handler.py +5 -5
  57. flwr/common/grpc.py +6 -6
  58. flwr/common/inflatable_protobuf_utils.py +1 -1
  59. flwr/common/inflatable_utils.py +38 -21
  60. flwr/common/logger.py +19 -19
  61. flwr/common/message.py +4 -4
  62. flwr/common/object_ref.py +7 -7
  63. flwr/common/record/array.py +3 -3
  64. flwr/common/record/arrayrecord.py +18 -30
  65. flwr/common/record/configrecord.py +3 -3
  66. flwr/common/record/recorddict.py +5 -5
  67. flwr/common/record/typeddict.py +9 -2
  68. flwr/common/recorddict_compat.py +7 -10
  69. flwr/common/retry_invoker.py +20 -20
  70. flwr/common/secure_aggregation/ndarrays_arithmetic.py +3 -3
  71. flwr/common/serde.py +11 -4
  72. flwr/common/serde_utils.py +2 -2
  73. flwr/common/telemetry.py +9 -5
  74. flwr/common/typing.py +58 -37
  75. flwr/compat/client/app.py +38 -37
  76. flwr/compat/client/grpc_client/connection.py +11 -11
  77. flwr/compat/server/app.py +5 -6
  78. flwr/proto/appio_pb2.py +13 -3
  79. flwr/proto/appio_pb2.pyi +134 -65
  80. flwr/proto/appio_pb2_grpc.py +20 -0
  81. flwr/proto/appio_pb2_grpc.pyi +27 -0
  82. flwr/proto/clientappio_pb2.py +17 -7
  83. flwr/proto/clientappio_pb2.pyi +15 -0
  84. flwr/proto/clientappio_pb2_grpc.py +206 -40
  85. flwr/proto/clientappio_pb2_grpc.pyi +168 -53
  86. flwr/proto/control_pb2.py +71 -52
  87. flwr/proto/control_pb2.pyi +277 -111
  88. flwr/proto/control_pb2_grpc.py +249 -40
  89. flwr/proto/control_pb2_grpc.pyi +185 -52
  90. flwr/proto/error_pb2.py +13 -3
  91. flwr/proto/error_pb2.pyi +24 -6
  92. flwr/proto/error_pb2_grpc.py +20 -0
  93. flwr/proto/error_pb2_grpc.pyi +27 -0
  94. flwr/proto/fab_pb2.py +14 -4
  95. flwr/proto/fab_pb2.pyi +59 -31
  96. flwr/proto/fab_pb2_grpc.py +20 -0
  97. flwr/proto/fab_pb2_grpc.pyi +27 -0
  98. flwr/proto/federation_pb2.py +38 -0
  99. flwr/proto/federation_pb2.pyi +56 -0
  100. flwr/proto/federation_pb2_grpc.py +24 -0
  101. flwr/proto/federation_pb2_grpc.pyi +31 -0
  102. flwr/proto/fleet_pb2.py +24 -14
  103. flwr/proto/fleet_pb2.pyi +141 -61
  104. flwr/proto/fleet_pb2_grpc.py +189 -48
  105. flwr/proto/fleet_pb2_grpc.pyi +175 -61
  106. flwr/proto/grpcadapter_pb2.py +14 -4
  107. flwr/proto/grpcadapter_pb2.pyi +38 -16
  108. flwr/proto/grpcadapter_pb2_grpc.py +35 -4
  109. flwr/proto/grpcadapter_pb2_grpc.pyi +38 -7
  110. flwr/proto/heartbeat_pb2.py +17 -7
  111. flwr/proto/heartbeat_pb2.pyi +51 -22
  112. flwr/proto/heartbeat_pb2_grpc.py +20 -0
  113. flwr/proto/heartbeat_pb2_grpc.pyi +27 -0
  114. flwr/proto/log_pb2.py +13 -3
  115. flwr/proto/log_pb2.pyi +34 -11
  116. flwr/proto/log_pb2_grpc.py +20 -0
  117. flwr/proto/log_pb2_grpc.pyi +27 -0
  118. flwr/proto/message_pb2.py +15 -5
  119. flwr/proto/message_pb2.pyi +154 -86
  120. flwr/proto/message_pb2_grpc.py +20 -0
  121. flwr/proto/message_pb2_grpc.pyi +27 -0
  122. flwr/proto/node_pb2.py +15 -5
  123. flwr/proto/node_pb2.pyi +50 -25
  124. flwr/proto/node_pb2_grpc.py +20 -0
  125. flwr/proto/node_pb2_grpc.pyi +27 -0
  126. flwr/proto/recorddict_pb2.py +13 -3
  127. flwr/proto/recorddict_pb2.pyi +184 -107
  128. flwr/proto/recorddict_pb2_grpc.py +20 -0
  129. flwr/proto/recorddict_pb2_grpc.pyi +27 -0
  130. flwr/proto/run_pb2.py +40 -31
  131. flwr/proto/run_pb2.pyi +158 -84
  132. flwr/proto/run_pb2_grpc.py +20 -0
  133. flwr/proto/run_pb2_grpc.pyi +27 -0
  134. flwr/proto/serverappio_pb2.py +13 -3
  135. flwr/proto/serverappio_pb2.pyi +32 -8
  136. flwr/proto/serverappio_pb2_grpc.py +246 -65
  137. flwr/proto/serverappio_pb2_grpc.pyi +221 -85
  138. flwr/proto/simulationio_pb2.py +16 -8
  139. flwr/proto/simulationio_pb2.pyi +15 -0
  140. flwr/proto/simulationio_pb2_grpc.py +162 -41
  141. flwr/proto/simulationio_pb2_grpc.pyi +149 -55
  142. flwr/proto/transport_pb2.py +20 -10
  143. flwr/proto/transport_pb2.pyi +249 -160
  144. flwr/proto/transport_pb2_grpc.py +35 -4
  145. flwr/proto/transport_pb2_grpc.pyi +38 -8
  146. flwr/server/app.py +39 -17
  147. flwr/server/client_manager.py +4 -5
  148. flwr/server/client_proxy.py +10 -11
  149. flwr/server/compat/app.py +4 -5
  150. flwr/server/compat/app_utils.py +2 -1
  151. flwr/server/compat/grid_client_proxy.py +10 -12
  152. flwr/server/compat/legacy_context.py +3 -4
  153. flwr/server/fleet_event_log_interceptor.py +2 -1
  154. flwr/server/grid/grid.py +2 -3
  155. flwr/server/grid/grpc_grid.py +10 -8
  156. flwr/server/grid/inmemory_grid.py +4 -4
  157. flwr/server/run_serverapp.py +2 -3
  158. flwr/server/server.py +34 -39
  159. flwr/server/server_app.py +7 -8
  160. flwr/server/server_config.py +1 -2
  161. flwr/server/serverapp/app.py +34 -28
  162. flwr/server/serverapp_components.py +4 -5
  163. flwr/server/strategy/aggregate.py +9 -8
  164. flwr/server/strategy/bulyan.py +13 -11
  165. flwr/server/strategy/dp_adaptive_clipping.py +16 -20
  166. flwr/server/strategy/dp_fixed_clipping.py +12 -17
  167. flwr/server/strategy/dpfedavg_adaptive.py +3 -4
  168. flwr/server/strategy/dpfedavg_fixed.py +6 -10
  169. flwr/server/strategy/fault_tolerant_fedavg.py +14 -13
  170. flwr/server/strategy/fedadagrad.py +18 -14
  171. flwr/server/strategy/fedadam.py +16 -14
  172. flwr/server/strategy/fedavg.py +16 -17
  173. flwr/server/strategy/fedavg_android.py +15 -15
  174. flwr/server/strategy/fedavgm.py +21 -18
  175. flwr/server/strategy/fedmedian.py +2 -3
  176. flwr/server/strategy/fedopt.py +11 -10
  177. flwr/server/strategy/fedprox.py +10 -9
  178. flwr/server/strategy/fedtrimmedavg.py +12 -11
  179. flwr/server/strategy/fedxgb_bagging.py +13 -11
  180. flwr/server/strategy/fedxgb_cyclic.py +6 -6
  181. flwr/server/strategy/fedxgb_nn_avg.py +4 -4
  182. flwr/server/strategy/fedyogi.py +16 -14
  183. flwr/server/strategy/krum.py +12 -11
  184. flwr/server/strategy/qfedavg.py +16 -15
  185. flwr/server/strategy/strategy.py +6 -9
  186. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +2 -1
  187. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -2
  188. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -4
  189. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +10 -12
  190. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +1 -3
  191. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +4 -4
  192. flwr/server/superlink/fleet/grpc_rere/node_auth_server_interceptor.py +3 -2
  193. flwr/server/superlink/fleet/message_handler/message_handler.py +75 -30
  194. flwr/server/superlink/fleet/rest_rere/rest_api.py +2 -2
  195. flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
  196. flwr/server/superlink/fleet/vce/backend/raybackend.py +5 -5
  197. flwr/server/superlink/fleet/vce/vce_api.py +15 -9
  198. flwr/server/superlink/linkstate/in_memory_linkstate.py +148 -149
  199. flwr/server/superlink/linkstate/linkstate.py +91 -43
  200. flwr/server/superlink/linkstate/linkstate_factory.py +22 -5
  201. flwr/server/superlink/linkstate/sqlite_linkstate.py +502 -436
  202. flwr/server/superlink/linkstate/utils.py +6 -6
  203. flwr/server/superlink/serverappio/serverappio_grpc.py +1 -2
  204. flwr/server/superlink/serverappio/serverappio_servicer.py +26 -21
  205. flwr/server/superlink/simulation/simulationio_grpc.py +1 -2
  206. flwr/server/superlink/simulation/simulationio_servicer.py +18 -13
  207. flwr/server/superlink/utils.py +4 -6
  208. flwr/server/typing.py +1 -1
  209. flwr/server/utils/tensorboard.py +15 -8
  210. flwr/server/workflow/default_workflows.py +5 -5
  211. flwr/server/workflow/secure_aggregation/secagg_workflow.py +2 -4
  212. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +8 -8
  213. flwr/serverapp/strategy/bulyan.py +16 -15
  214. flwr/serverapp/strategy/dp_adaptive_clipping.py +12 -11
  215. flwr/serverapp/strategy/dp_fixed_clipping.py +11 -14
  216. flwr/serverapp/strategy/fedadagrad.py +10 -11
  217. flwr/serverapp/strategy/fedadam.py +10 -11
  218. flwr/serverapp/strategy/fedavg.py +9 -10
  219. flwr/serverapp/strategy/fedavgm.py +17 -16
  220. flwr/serverapp/strategy/fedmedian.py +2 -2
  221. flwr/serverapp/strategy/fedopt.py +10 -11
  222. flwr/serverapp/strategy/fedprox.py +7 -8
  223. flwr/serverapp/strategy/fedtrimmedavg.py +9 -9
  224. flwr/serverapp/strategy/fedxgb_bagging.py +3 -3
  225. flwr/serverapp/strategy/fedxgb_cyclic.py +9 -9
  226. flwr/serverapp/strategy/fedyogi.py +9 -11
  227. flwr/serverapp/strategy/krum.py +7 -7
  228. flwr/serverapp/strategy/multikrum.py +9 -9
  229. flwr/serverapp/strategy/qfedavg.py +17 -16
  230. flwr/serverapp/strategy/strategy.py +6 -9
  231. flwr/serverapp/strategy/strategy_utils.py +7 -8
  232. flwr/simulation/app.py +46 -42
  233. flwr/simulation/legacy_app.py +12 -12
  234. flwr/simulation/ray_transport/ray_actor.py +10 -11
  235. flwr/simulation/ray_transport/ray_client_proxy.py +11 -12
  236. flwr/simulation/run_simulation.py +43 -43
  237. flwr/simulation/simulationio_connection.py +4 -4
  238. flwr/supercore/cli/flower_superexec.py +3 -4
  239. flwr/supercore/constant.py +34 -1
  240. flwr/supercore/corestate/corestate.py +24 -3
  241. flwr/supercore/corestate/in_memory_corestate.py +138 -0
  242. flwr/supercore/corestate/sqlite_corestate.py +157 -0
  243. flwr/supercore/ffs/disk_ffs.py +1 -2
  244. flwr/supercore/ffs/ffs.py +1 -2
  245. flwr/supercore/ffs/ffs_factory.py +1 -2
  246. flwr/{common → supercore}/heartbeat.py +20 -25
  247. flwr/supercore/object_store/in_memory_object_store.py +1 -2
  248. flwr/supercore/object_store/object_store.py +1 -2
  249. flwr/supercore/object_store/object_store_factory.py +1 -2
  250. flwr/supercore/object_store/sqlite_object_store.py +8 -7
  251. flwr/supercore/primitives/asymmetric.py +1 -1
  252. flwr/supercore/primitives/asymmetric_ed25519.py +11 -1
  253. flwr/supercore/sqlite_mixin.py +37 -34
  254. flwr/supercore/superexec/plugin/base_exec_plugin.py +1 -2
  255. flwr/supercore/superexec/plugin/exec_plugin.py +3 -3
  256. flwr/supercore/superexec/run_superexec.py +9 -13
  257. flwr/supercore/utils.py +190 -0
  258. flwr/superlink/artifact_provider/artifact_provider.py +1 -2
  259. flwr/superlink/auth_plugin/auth_plugin.py +6 -9
  260. flwr/superlink/auth_plugin/noop_auth_plugin.py +6 -9
  261. flwr/{cli/new/templates/app → superlink/federation}/__init__.py +10 -1
  262. flwr/superlink/federation/federation_manager.py +64 -0
  263. flwr/superlink/federation/noop_federation_manager.py +71 -0
  264. flwr/superlink/servicer/control/control_account_auth_interceptor.py +22 -13
  265. flwr/superlink/servicer/control/control_event_log_interceptor.py +7 -7
  266. flwr/superlink/servicer/control/control_grpc.py +7 -6
  267. flwr/superlink/servicer/control/control_license_interceptor.py +3 -3
  268. flwr/superlink/servicer/control/control_servicer.py +190 -23
  269. flwr/supernode/cli/flower_supernode.py +58 -3
  270. flwr/supernode/nodestate/in_memory_nodestate.py +121 -49
  271. flwr/supernode/nodestate/nodestate.py +52 -8
  272. flwr/supernode/nodestate/nodestate_factory.py +7 -4
  273. flwr/supernode/runtime/run_clientapp.py +41 -22
  274. flwr/supernode/servicer/clientappio/clientappio_servicer.py +46 -10
  275. flwr/supernode/start_client_internal.py +165 -46
  276. {flwr-1.23.0.dist-info → flwr-1.25.0.dist-info}/METADATA +9 -11
  277. flwr-1.25.0.dist-info/RECORD +393 -0
  278. flwr/cli/new/templates/app/.gitignore.tpl +0 -163
  279. flwr/cli/new/templates/app/LICENSE.tpl +0 -202
  280. flwr/cli/new/templates/app/README.baseline.md.tpl +0 -127
  281. flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -68
  282. flwr/cli/new/templates/app/README.md.tpl +0 -37
  283. flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -1
  284. flwr/cli/new/templates/app/code/__init__.py +0 -15
  285. flwr/cli/new/templates/app/code/__init__.py.tpl +0 -1
  286. flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +0 -1
  287. flwr/cli/new/templates/app/code/client.baseline.py.tpl +0 -75
  288. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -93
  289. flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -71
  290. flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -102
  291. flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -46
  292. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -80
  293. flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +0 -55
  294. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -108
  295. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -82
  296. flwr/cli/new/templates/app/code/client.xgboost.py.tpl +0 -110
  297. flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -36
  298. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -92
  299. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -87
  300. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -56
  301. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -73
  302. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -78
  303. flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -66
  304. flwr/cli/new/templates/app/code/server.baseline.py.tpl +0 -43
  305. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -42
  306. flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -39
  307. flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -41
  308. flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -38
  309. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -41
  310. flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +0 -31
  311. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -44
  312. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -38
  313. flwr/cli/new/templates/app/code/server.xgboost.py.tpl +0 -56
  314. flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -1
  315. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -98
  316. flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -57
  317. flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -102
  318. flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -7
  319. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -98
  320. flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +0 -111
  321. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -67
  322. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -52
  323. flwr/cli/new/templates/app/code/task.xgboost.py.tpl +0 -67
  324. flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -1
  325. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +0 -146
  326. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +0 -80
  327. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +0 -65
  328. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +0 -52
  329. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +0 -56
  330. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -49
  331. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -53
  332. flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +0 -53
  333. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +0 -52
  334. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -53
  335. flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +0 -61
  336. flwr/supercore/object_store/utils.py +0 -43
  337. flwr-1.23.0.dist-info/RECORD +0 -439
  338. {flwr-1.23.0.dist-info → flwr-1.25.0.dist-info}/WHEEL +0 -0
  339. {flwr-1.23.0.dist-info → flwr-1.25.0.dist-info}/entry_points.txt +0 -0
@@ -17,7 +17,6 @@
17
17
 
18
18
  from collections.abc import Sequence
19
19
  from pathlib import Path
20
- from typing import Optional, Union
21
20
 
22
21
  from flwr.common.typing import AccountAuthCredentials, AccountAuthLoginDetails
23
22
  from flwr.proto.control_pb2_grpc import ControlStub
@@ -26,33 +25,77 @@ from .auth_plugin import CliAuthPlugin, LoginError
26
25
 
27
26
 
28
27
  class NoOpCliAuthPlugin(CliAuthPlugin):
29
- """No-operation implementation of the CliAuthPlugin."""
28
+ """No-operation implementation of the CliAuthPlugin.
29
+
30
+ This plugin is used when account authentication is not enabled. It provides stub
31
+ implementations of all authentication methods that perform no actions.
32
+ """
30
33
 
31
34
  @staticmethod
32
35
  def login(
33
36
  login_details: AccountAuthLoginDetails,
34
37
  control_stub: ControlStub,
35
38
  ) -> AccountAuthCredentials:
36
- """Raise LoginError as no-op plugin does not support login."""
39
+ """Raise LoginError as no-op plugin does not support login.
40
+
41
+ Parameters
42
+ ----------
43
+ login_details : AccountAuthLoginDetails
44
+ Login details (unused).
45
+ control_stub : ControlStub
46
+ Control stub (unused).
47
+
48
+ Returns
49
+ -------
50
+ AccountAuthCredentials
51
+ This method never returns as it always raises an exception.
52
+
53
+ Raises
54
+ ------
55
+ LoginError
56
+ Always raised to indicate authentication is not enabled.
57
+ """
37
58
  raise LoginError("Account authentication is not enabled on this SuperLink.")
38
59
 
39
60
  def __init__(self, credentials_path: Path) -> None:
40
61
  pass
41
62
 
42
63
  def store_tokens(self, credentials: AccountAuthCredentials) -> None:
43
- """Do nothing."""
64
+ """Do nothing (no-op implementation)."""
44
65
 
45
66
  def load_tokens(self) -> None:
46
- """Do nothing."""
67
+ """Do nothing (no-op implementation)."""
47
68
 
48
69
  def write_tokens_to_metadata(
49
- self, metadata: Sequence[tuple[str, Union[str, bytes]]]
50
- ) -> Sequence[tuple[str, Union[str, bytes]]]:
51
- """Return the metadata unchanged."""
70
+ self, metadata: Sequence[tuple[str, str | bytes]]
71
+ ) -> Sequence[tuple[str, str | bytes]]:
72
+ """Return the metadata unchanged.
73
+
74
+ Parameters
75
+ ----------
76
+ metadata : Sequence[tuple[str, str | bytes]]
77
+ The original metadata.
78
+
79
+ Returns
80
+ -------
81
+ Sequence[tuple[str, str | bytes]]
82
+ The same metadata, unmodified.
83
+ """
52
84
  return metadata
53
85
 
54
86
  def read_tokens_from_metadata(
55
- self, metadata: Sequence[tuple[str, Union[str, bytes]]]
56
- ) -> Optional[AccountAuthCredentials]:
57
- """Return None."""
87
+ self, metadata: Sequence[tuple[str, str | bytes]]
88
+ ) -> AccountAuthCredentials | None:
89
+ """Return None (no tokens to read).
90
+
91
+ Parameters
92
+ ----------
93
+ metadata : Sequence[tuple[str, str | bytes]]
94
+ The metadata to read from (unused).
95
+
96
+ Returns
97
+ -------
98
+ None
99
+ Always returns None as no authentication is performed.
100
+ """
58
101
  return None
@@ -19,7 +19,7 @@ import json
19
19
  import time
20
20
  from collections.abc import Sequence
21
21
  from pathlib import Path
22
- from typing import Any, Optional, Union
22
+ from typing import Any
23
23
 
24
24
  import typer
25
25
 
@@ -40,11 +40,15 @@ from .auth_plugin import CliAuthPlugin, LoginError
40
40
 
41
41
 
42
42
  class OidcCliPlugin(CliAuthPlugin):
43
- """Flower OIDC auth plugin for CLI."""
43
+ """Flower OIDC authentication plugin for CLI.
44
+
45
+ This plugin implements OpenID Connect (OIDC) device flow authentication for CLI
46
+ access to Flower SuperLink.
47
+ """
44
48
 
45
49
  def __init__(self, credentials_path: Path):
46
- self.access_token: Optional[str] = None
47
- self.refresh_token: Optional[str] = None
50
+ self.access_token: str | None = None
51
+ self.refresh_token: str | None = None
48
52
  self.credentials_path = credentials_path
49
53
 
50
54
  @staticmethod
@@ -52,7 +56,25 @@ class OidcCliPlugin(CliAuthPlugin):
52
56
  login_details: AccountAuthLoginDetails,
53
57
  control_stub: ControlStub,
54
58
  ) -> AccountAuthCredentials:
55
- """Authenticate the account and retrieve authentication credentials."""
59
+ """Authenticate the account and retrieve authentication credentials.
60
+
61
+ Parameters
62
+ ----------
63
+ login_details : AccountAuthLoginDetails
64
+ Login details containing device code and verification URI.
65
+ control_stub : ControlStub
66
+ Control stub for making authentication requests.
67
+
68
+ Returns
69
+ -------
70
+ AccountAuthCredentials
71
+ The access and refresh tokens.
72
+
73
+ Raises
74
+ ------
75
+ LoginError
76
+ If authentication times out.
77
+ """
56
78
  typer.secho(
57
79
  "Please log into your Flower account here: "
58
80
  f"{login_details.verification_uri_complete}",
@@ -108,14 +130,15 @@ class OidcCliPlugin(CliAuthPlugin):
108
130
  self.refresh_token = refresh_token
109
131
 
110
132
  def write_tokens_to_metadata(
111
- self, metadata: Sequence[tuple[str, Union[str, bytes]]]
112
- ) -> Sequence[tuple[str, Union[str, bytes]]]:
133
+ self, metadata: Sequence[tuple[str, str | bytes]]
134
+ ) -> Sequence[tuple[str, str | bytes]]:
113
135
  """Write authentication tokens to the provided metadata."""
114
136
  if self.access_token is None or self.refresh_token is None:
115
137
  typer.secho(
116
138
  "❌ Missing authentication tokens. Please login first.",
117
139
  fg=typer.colors.RED,
118
140
  bold=True,
141
+ err=True,
119
142
  )
120
143
  raise typer.Exit(code=1)
121
144
 
@@ -125,8 +148,8 @@ class OidcCliPlugin(CliAuthPlugin):
125
148
  ]
126
149
 
127
150
  def read_tokens_from_metadata(
128
- self, metadata: Sequence[tuple[str, Union[str, bytes]]]
129
- ) -> Optional[AccountAuthCredentials]:
151
+ self, metadata: Sequence[tuple[str, str | bytes]]
152
+ ) -> AccountAuthCredentials | None:
130
153
  """Read authentication tokens from the provided metadata."""
131
154
  metadata_dict = dict(metadata)
132
155
  access_token = metadata_dict.get(ACCESS_TOKEN_KEY)
flwr/cli/build.py CHANGED
@@ -17,10 +17,9 @@
17
17
 
18
18
  import hashlib
19
19
  import zipfile
20
- from collections.abc import Iterable
21
20
  from io import BytesIO
22
21
  from pathlib import Path
23
- from typing import Annotated, Any, Optional, Union
22
+ from typing import Annotated, Any
24
23
 
25
24
  import pathspec
26
25
  import tomli
@@ -37,13 +36,28 @@ from flwr.common.constant import (
37
36
  )
38
37
 
39
38
  from .config_utils import load_and_validate
40
- from .utils import is_valid_project_name
39
+ from .utils import build_pathspec, is_valid_project_name, load_gitignore_patterns
41
40
 
42
41
 
43
42
  def write_to_zip(
44
- zipfile_obj: zipfile.ZipFile, filename: str, contents: Union[bytes, str]
43
+ zipfile_obj: zipfile.ZipFile, filename: str, contents: bytes | str
45
44
  ) -> zipfile.ZipFile:
46
- """Set a fixed date and write contents to a zip file."""
45
+ """Set a fixed date and write contents to a zip file.
46
+
47
+ Parameters
48
+ ----------
49
+ zipfile_obj : zipfile.ZipFile
50
+ The ZipFile object to write to.
51
+ filename : str
52
+ Name of the file within the zip archive.
53
+ contents : bytes | str
54
+ The file contents to write.
55
+
56
+ Returns
57
+ -------
58
+ ZipFile
59
+ The modified ZipFile object.
60
+ """
47
61
  zip_info = zipfile.ZipInfo(filename)
48
62
  zip_info.date_time = FAB_DATE
49
63
  zipfile_obj.writestr(zip_info, contents)
@@ -51,7 +65,21 @@ def write_to_zip(
51
65
 
52
66
 
53
67
  def get_fab_filename(config: dict[str, Any], fab_hash: str) -> str:
54
- """Get the FAB filename based on the given config and FAB hash."""
68
+ """Get the FAB filename based on the given config and FAB hash.
69
+
70
+ Parameters
71
+ ----------
72
+ config : dict[str, Any]
73
+ The project configuration dictionary.
74
+ fab_hash : str
75
+ The SHA-256 hash of the FAB file.
76
+
77
+ Returns
78
+ -------
79
+ str
80
+ The formatted FAB filename in the pattern:
81
+ <publisher>.<name>.<version>.<hash_prefix>.fab
82
+ """
55
83
  publisher = config["tool"]["flwr"]["app"]["publisher"]
56
84
  name = config["project"]["name"]
57
85
  version = config["project"]["version"].replace(".", "-")
@@ -62,7 +90,7 @@ def get_fab_filename(config: dict[str, Any], fab_hash: str) -> str:
62
90
  # pylint: disable=too-many-locals, too-many-statements
63
91
  def build(
64
92
  app: Annotated[
65
- Optional[Path],
93
+ Path | None,
66
94
  typer.Option(help="Path of the Flower App to bundle into a FAB"),
67
95
  ] = None,
68
96
  ) -> None:
@@ -83,6 +111,7 @@ def build(
83
111
  f"❌ The path {app} is not a valid path to a Flower app.",
84
112
  fg=typer.colors.RED,
85
113
  bold=True,
114
+ err=True,
86
115
  )
87
116
  raise typer.Exit(code=1)
88
117
 
@@ -93,6 +122,7 @@ def build(
93
122
  "and can only contain letters, digits, and hyphens.",
94
123
  fg=typer.colors.RED,
95
124
  bold=True,
125
+ err=True,
96
126
  )
97
127
  raise typer.Exit(code=1)
98
128
 
@@ -103,6 +133,7 @@ def build(
103
133
  + "\n".join([f"- {line}" for line in errors]),
104
134
  fg=typer.colors.RED,
105
135
  bold=True,
136
+ err=True,
106
137
  )
107
138
  raise typer.Exit(code=1)
108
139
 
@@ -152,7 +183,7 @@ def build_fab_from_disk(app: Path) -> bytes:
152
183
  all_files = [f for f in app.rglob("*") if f.is_file()]
153
184
 
154
185
  # Create dict mapping relative paths to Path objects
155
- files_dict: dict[str, Union[bytes, Path]] = {
186
+ files_dict: dict[str, bytes | Path] = {
156
187
  # Ensure consistent path separators across platforms
157
188
  str(file_path.relative_to(app)).replace("\\", "/"): file_path
158
189
  for file_path in all_files
@@ -162,7 +193,7 @@ def build_fab_from_disk(app: Path) -> bytes:
162
193
  return build_fab_from_files(files_dict)
163
194
 
164
195
 
165
- def build_fab_from_files(files: dict[str, Union[bytes, Path]]) -> bytes:
196
+ def build_fab_from_files(files: dict[str, bytes | Path]) -> bytes:
166
197
  r"""Build a FAB from in-memory files and return the FAB as bytes.
167
198
 
168
199
  This is the core FAB building function that works with in-memory data.
@@ -196,7 +227,7 @@ def build_fab_from_files(files: dict[str, Union[bytes, Path]]) -> bytes:
196
227
  fab_bytes = build_fab_from_files(files)
197
228
  """
198
229
 
199
- def to_bytes(content: Union[bytes, Path]) -> bytes:
230
+ def to_bytes(content: bytes | Path) -> bytes:
200
231
  return content.read_bytes() if isinstance(content, Path) else content
201
232
 
202
233
  # Extract, load, and parse pyproject.toml
@@ -267,23 +298,34 @@ def build_fab_from_files(files: dict[str, Union[bytes, Path]]) -> bytes:
267
298
  return fab_bytes
268
299
 
269
300
 
270
- def build_pathspec(patterns: Iterable[str]) -> pathspec.PathSpec:
271
- """Build a PathSpec from a list of patterns."""
272
- return pathspec.PathSpec.from_lines("gitwildmatch", patterns)
273
-
274
-
275
301
  def get_fab_include_pathspec() -> pathspec.PathSpec:
276
- """Get the PathSpec for files to include in a FAB."""
302
+ """Get the PathSpec for files to include in a FAB.
303
+
304
+ Returns
305
+ -------
306
+ PathSpec
307
+ PathSpec object with default include patterns for FAB files.
308
+ """
277
309
  return build_pathspec(FAB_INCLUDE_PATTERNS)
278
310
 
279
311
 
280
- def get_fab_exclude_pathspec(gitignore_content: Optional[bytes]) -> pathspec.PathSpec:
312
+ def get_fab_exclude_pathspec(gitignore_content: bytes | None) -> pathspec.PathSpec:
281
313
  """Get the PathSpec for files to exclude from a FAB.
282
314
 
283
315
  If gitignore_content is provided, its patterns will be combined with the default
284
316
  exclude patterns.
317
+
318
+ Parameters
319
+ ----------
320
+ gitignore_content : bytes | None
321
+ Optional gitignore file content as bytes.
322
+
323
+ Returns
324
+ -------
325
+ PathSpec
326
+ PathSpec object with combined exclude patterns.
285
327
  """
286
328
  patterns = list(FAB_EXCLUDE_PATTERNS)
287
329
  if gitignore_content:
288
- patterns += gitignore_content.decode("UTF-8").splitlines()
330
+ patterns += load_gitignore_patterns(gitignore_content)
289
331
  return pathspec.PathSpec.from_lines("gitwildmatch", patterns)
@@ -15,7 +15,8 @@
15
15
  """Flower run interceptor."""
16
16
 
17
17
 
18
- from typing import Any, Callable, Union
18
+ from collections.abc import Callable
19
+ from typing import Any
19
20
 
20
21
  import grpc
21
22
 
@@ -26,16 +27,17 @@ from flwr.proto.control_pb2 import ( # pylint: disable=E0611
26
27
 
27
28
  from .auth_plugin import CliAuthPlugin
28
29
 
29
- Request = Union[
30
- StartRunRequest,
31
- StreamLogsRequest,
32
- ]
30
+ Request = StartRunRequest | StreamLogsRequest
33
31
 
34
32
 
35
33
  class CliAccountAuthInterceptor(
36
34
  grpc.UnaryUnaryClientInterceptor, grpc.UnaryStreamClientInterceptor # type: ignore
37
35
  ):
38
- """CLI interceptor for account authentication."""
36
+ """CLI interceptor for account authentication.
37
+
38
+ This interceptor adds authentication tokens to gRPC metadata for CLI requests and
39
+ handles token refresh from response metadata.
40
+ """
39
41
 
40
42
  def __init__(self, auth_plugin: CliAuthPlugin):
41
43
  self.auth_plugin = auth_plugin
@@ -46,7 +48,22 @@ class CliAccountAuthInterceptor(
46
48
  client_call_details: grpc.ClientCallDetails,
47
49
  request: Request,
48
50
  ) -> grpc.Call:
49
- """Send and receive tokens via metadata."""
51
+ """Send and receive tokens via metadata.
52
+
53
+ Parameters
54
+ ----------
55
+ continuation : Callable[[Any, Any], Any]
56
+ The next interceptor or handler in the chain.
57
+ client_call_details : grpc.ClientCallDetails
58
+ Details of the RPC call as a NamedTuple.
59
+ request : Request
60
+ The RPC request object.
61
+
62
+ Returns
63
+ -------
64
+ grpc.Call
65
+ The RPC response.
66
+ """
50
67
  new_metadata = self.auth_plugin.write_tokens_to_metadata(
51
68
  client_call_details.metadata or []
52
69
  )
flwr/cli/config_utils.py CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
 
18
18
  from pathlib import Path
19
- from typing import Any, Optional, Union
19
+ from typing import Any
20
20
 
21
21
  import tomli
22
22
  import typer
@@ -30,7 +30,7 @@ from flwr.common.config import (
30
30
  )
31
31
 
32
32
 
33
- def get_fab_metadata(fab_file: Union[Path, bytes]) -> tuple[str, str]:
33
+ def get_fab_metadata(fab_file: Path | bytes) -> tuple[str, str]:
34
34
  """Extract the fab_id and the fab_version from a FAB file or path.
35
35
 
36
36
  Parameters
@@ -48,9 +48,9 @@ def get_fab_metadata(fab_file: Union[Path, bytes]) -> tuple[str, str]:
48
48
 
49
49
 
50
50
  def load_and_validate(
51
- path: Optional[Path] = None,
51
+ path: Path | None = None,
52
52
  check_module: bool = True,
53
- ) -> tuple[Optional[dict[str, Any]], list[str], list[str]]:
53
+ ) -> tuple[dict[str, Any] | None, list[str], list[str]]:
54
54
  """Load and validate pyproject.toml as dict.
55
55
 
56
56
  Parameters
@@ -89,8 +89,20 @@ def load_and_validate(
89
89
  return (config, errors, warnings)
90
90
 
91
91
 
92
- def load(toml_path: Path) -> Optional[dict[str, Any]]:
93
- """Load pyproject.toml and return as dict."""
92
+ def load(toml_path: Path) -> dict[str, Any] | None:
93
+ """Load pyproject.toml and return as dict.
94
+
95
+ Parameters
96
+ ----------
97
+ toml_path : Path
98
+ Path to the pyproject.toml file.
99
+
100
+ Returns
101
+ -------
102
+ dict[str, Any] | None
103
+ Parsed TOML configuration as dictionary, or None if file doesn't exist
104
+ or has invalid TOML syntax.
105
+ """
94
106
  if not toml_path.is_file():
95
107
  return None
96
108
 
@@ -102,12 +114,31 @@ def load(toml_path: Path) -> Optional[dict[str, Any]]:
102
114
 
103
115
 
104
116
  def process_loaded_project_config(
105
- config: Union[dict[str, Any], None], errors: list[str], warnings: list[str]
117
+ config: dict[str, Any] | None, errors: list[str], warnings: list[str]
106
118
  ) -> dict[str, Any]:
107
119
  """Process and return the loaded project configuration.
108
120
 
109
121
  This function handles errors and warnings from the `load_and_validate` function,
110
122
  exits on critical issues, and returns the validated configuration.
123
+
124
+ Parameters
125
+ ----------
126
+ config : dict[str, Any] | None
127
+ The loaded configuration dictionary, or None if loading failed.
128
+ errors : list[str]
129
+ List of error messages from validation.
130
+ warnings : list[str]
131
+ List of warning messages from validation.
132
+
133
+ Returns
134
+ -------
135
+ dict[str, Any]
136
+ The validated configuration dictionary.
137
+
138
+ Raises
139
+ ------
140
+ typer.Exit
141
+ If config is None or contains critical errors.
111
142
  """
112
143
  if config is None:
113
144
  typer.secho(
@@ -116,6 +147,7 @@ def process_loaded_project_config(
116
147
  + "\n".join([f"- {line}" for line in errors]),
117
148
  fg=typer.colors.RED,
118
149
  bold=True,
150
+ err=True,
119
151
  )
120
152
  raise typer.Exit(code=1)
121
153
 
@@ -133,11 +165,32 @@ def process_loaded_project_config(
133
165
 
134
166
 
135
167
  def validate_federation_in_project_config(
136
- federation: Optional[str],
168
+ federation: str | None,
137
169
  config: dict[str, Any],
138
- overrides: Optional[list[str]] = None,
170
+ overrides: list[str] | None = None,
139
171
  ) -> tuple[str, dict[str, Any]]:
140
- """Validate the federation name in the Flower project configuration."""
172
+ """Validate the federation name in the Flower project configuration.
173
+
174
+ Parameters
175
+ ----------
176
+ federation : str | None
177
+ Name of the federation, or None to use default from config.
178
+ config : dict[str, Any]
179
+ The project configuration dictionary.
180
+ overrides : list[str] | None
181
+ List of configuration override strings. Default is None.
182
+
183
+ Returns
184
+ -------
185
+ tuple[str, dict[str, Any]]
186
+ A tuple of (federation_name, federation_config).
187
+
188
+ Raises
189
+ ------
190
+ typer.Exit
191
+ If no federation name provided and no default found, or if federation
192
+ doesn't exist in config.
193
+ """
141
194
  federation = federation or config["tool"]["flwr"]["federations"].get("default")
142
195
 
143
196
  if federation is None:
@@ -147,6 +200,7 @@ def validate_federation_in_project_config(
147
200
  "`options.num-supernodes` value).",
148
201
  fg=typer.colors.RED,
149
202
  bold=True,
203
+ err=True,
150
204
  )
151
205
  raise typer.Exit(code=1)
152
206
 
@@ -162,6 +216,7 @@ def validate_federation_in_project_config(
162
216
  + "\n".join(available_feds),
163
217
  fg=typer.colors.RED,
164
218
  bold=True,
219
+ err=True,
165
220
  )
166
221
  raise typer.Exit(code=1)
167
222
 
@@ -175,7 +230,7 @@ def validate_federation_in_project_config(
175
230
 
176
231
  def validate_certificate_in_federation_config(
177
232
  app: Path, federation_config: dict[str, Any]
178
- ) -> tuple[bool, Optional[bytes]]:
233
+ ) -> tuple[bool, bytes | None]:
179
234
  """Validate the certificates in the Flower project configuration.
180
235
 
181
236
  Accepted configurations:
@@ -207,6 +262,7 @@ def validate_certificate_in_federation_config(
207
262
  "is set to `True`.",
208
263
  fg=typer.colors.RED,
209
264
  bold=True,
265
+ err=True,
210
266
  )
211
267
  raise typer.Exit(code=1)
212
268
 
@@ -218,6 +274,7 @@ def validate_certificate_in_federation_config(
218
274
  f"❌ Failed to read certificate file `{root_certificates}`: {e}",
219
275
  fg=typer.colors.RED,
220
276
  bold=True,
277
+ err=True,
221
278
  )
222
279
  raise typer.Exit(code=1) from e
223
280
  else:
@@ -227,19 +284,49 @@ def validate_certificate_in_federation_config(
227
284
 
228
285
 
229
286
  def exit_if_no_address(federation_config: dict[str, Any], cmd: str) -> None:
230
- """Exit if the provided federation_config has no "address" key."""
287
+ """Exit if the provided federation_config has no "address" key.
288
+
289
+ Parameters
290
+ ----------
291
+ federation_config : dict[str, Any]
292
+ The federation configuration dictionary to check.
293
+ cmd : str
294
+ The command name to display in the error message.
295
+
296
+ Raises
297
+ ------
298
+ typer.Exit
299
+ If 'address' key is not present in federation_config.
300
+ """
231
301
  if "address" not in federation_config:
232
302
  typer.secho(
233
303
  f"❌ `flwr {cmd}` currently works with a SuperLink. Ensure that the "
234
304
  "correct SuperLink (Control API) address is provided in `pyproject.toml`.",
235
305
  fg=typer.colors.RED,
236
306
  bold=True,
307
+ err=True,
237
308
  )
238
309
  raise typer.Exit(code=1)
239
310
 
240
311
 
241
312
  def get_insecure_flag(federation_config: dict[str, Any]) -> bool:
242
- """Extract and validate the `insecure` flag from the federation configuration."""
313
+ """Extract and validate the `insecure` flag from the federation configuration.
314
+
315
+ Parameters
316
+ ----------
317
+ federation_config : dict[str, Any]
318
+ The federation configuration dictionary.
319
+
320
+ Returns
321
+ -------
322
+ bool
323
+ The insecure flag value. Returns False if not specified.
324
+
325
+ Raises
326
+ ------
327
+ typer.Exit
328
+ If insecure value is not a boolean type.
329
+ """
243
330
  insecure_value = federation_config.get("insecure")
244
331
 
245
332
  if insecure_value is None:
@@ -252,5 +339,6 @@ def get_insecure_flag(federation_config: dict[str, Any]) -> bool:
252
339
  "(`insecure = true` or `insecure = false`)",
253
340
  fg=typer.colors.RED,
254
341
  bold=True,
342
+ err=True,
255
343
  )
256
344
  raise typer.Exit(code=1)
@@ -12,4 +12,13 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
- """Flower CLI `new` command app / code / flwr_tune templates."""
15
+ """Flower command line interface `federation` command."""
16
+
17
+
18
+ from .ls import ls as ls
19
+ from .show import show as show
20
+
21
+ __all__ = [
22
+ "ls",
23
+ "show",
24
+ ]