flwr-nightly 1.23.0.dev20250930__py3-none-any.whl → 1.26.0.dev20260121__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 (375) hide show
  1. flwr/__init__.py +17 -6
  2. flwr/app/__init__.py +4 -1
  3. flwr/app/error.py +2 -2
  4. flwr/app/exception.py +3 -3
  5. flwr/app/message_type.py +29 -0
  6. flwr/app/metadata.py +5 -2
  7. flwr/app/user_config.py +19 -0
  8. flwr/cli/app.py +62 -9
  9. flwr/cli/{new/templates/app/code → app_cmd}/__init__.py +9 -1
  10. flwr/cli/app_cmd/publish.py +285 -0
  11. flwr/cli/app_cmd/review.py +262 -0
  12. flwr/cli/auth_plugin/__init__.py +13 -6
  13. flwr/cli/auth_plugin/auth_plugin.py +26 -15
  14. flwr/cli/auth_plugin/noop_auth_plugin.py +101 -0
  15. flwr/cli/auth_plugin/oidc_cli_plugin.py +52 -32
  16. flwr/cli/build.py +166 -53
  17. flwr/cli/{cli_user_auth_interceptor.py → cli_account_auth_interceptor.py} +27 -10
  18. flwr/cli/config/__init__.py +21 -0
  19. flwr/cli/config/ls.py +104 -0
  20. flwr/cli/config_migration.py +300 -0
  21. flwr/cli/config_utils.py +154 -13
  22. flwr/cli/constant.py +67 -0
  23. flwr/cli/{new/templates/app/code/flwr_tune → federation}/__init__.py +8 -1
  24. flwr/cli/federation/ls.py +361 -0
  25. flwr/cli/flower_config.py +447 -0
  26. flwr/cli/install.py +91 -13
  27. flwr/cli/log.py +65 -36
  28. flwr/cli/login/login.py +41 -27
  29. flwr/cli/ls.py +232 -158
  30. flwr/cli/new/new.py +188 -244
  31. flwr/cli/pull.py +25 -34
  32. flwr/cli/run/run.py +106 -74
  33. flwr/cli/run_utils.py +148 -0
  34. flwr/cli/stop.py +46 -37
  35. flwr/cli/supernode/__init__.py +25 -0
  36. flwr/cli/supernode/ls.py +273 -0
  37. flwr/cli/supernode/register.py +190 -0
  38. flwr/cli/supernode/unregister.py +140 -0
  39. flwr/cli/typing.py +211 -0
  40. flwr/cli/utils.py +428 -80
  41. flwr/client/__init__.py +2 -1
  42. flwr/client/dpfedavg_numpy_client.py +4 -1
  43. flwr/client/grpc_adapter_client/connection.py +14 -17
  44. flwr/client/grpc_rere_client/connection.py +73 -43
  45. flwr/client/grpc_rere_client/grpc_adapter.py +35 -15
  46. flwr/client/grpc_rere_client/{client_interceptor.py → node_auth_client_interceptor.py} +5 -7
  47. flwr/client/message_handler/message_handler.py +4 -3
  48. flwr/client/mod/centraldp_mods.py +1 -1
  49. flwr/client/mod/localdp_mod.py +1 -1
  50. flwr/client/mod/secure_aggregation/secaggplus_mod.py +11 -9
  51. flwr/client/numpy_client.py +1 -1
  52. flwr/client/rest_client/connection.py +99 -54
  53. flwr/client/run_info_store.py +6 -6
  54. flwr/client/typing.py +1 -1
  55. flwr/clientapp/__init__.py +1 -2
  56. flwr/{client → clientapp}/client_app.py +11 -11
  57. flwr/clientapp/mod/centraldp_mods.py +16 -17
  58. flwr/clientapp/mod/localdp_mod.py +8 -9
  59. flwr/clientapp/typing.py +1 -1
  60. flwr/{client/clientapp → clientapp}/utils.py +4 -4
  61. flwr/common/__init__.py +3 -2
  62. flwr/common/args.py +3 -4
  63. flwr/common/config.py +15 -17
  64. flwr/common/constant.py +56 -28
  65. flwr/common/context.py +2 -1
  66. flwr/common/differential_privacy.py +3 -4
  67. flwr/common/event_log_plugin/event_log_plugin.py +3 -4
  68. flwr/common/exit/exit.py +16 -3
  69. flwr/common/exit/exit_code.py +39 -10
  70. flwr/common/exit/exit_handler.py +6 -2
  71. flwr/common/exit/signal_handler.py +5 -5
  72. flwr/common/grpc.py +8 -7
  73. flwr/common/inflatable_protobuf_utils.py +1 -1
  74. flwr/common/inflatable_utils.py +48 -31
  75. flwr/common/logger.py +19 -19
  76. flwr/common/message.py +5 -5
  77. flwr/common/object_ref.py +7 -7
  78. flwr/common/record/array.py +6 -6
  79. flwr/common/record/arrayrecord.py +18 -21
  80. flwr/common/record/configrecord.py +3 -3
  81. flwr/common/record/recorddict.py +5 -5
  82. flwr/common/record/typeddict.py +9 -2
  83. flwr/common/recorddict_compat.py +7 -10
  84. flwr/common/retry_invoker.py +20 -20
  85. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -89
  86. flwr/common/secure_aggregation/ndarrays_arithmetic.py +8 -5
  87. flwr/common/serde.py +22 -11
  88. flwr/common/serde_utils.py +2 -2
  89. flwr/common/telemetry.py +10 -6
  90. flwr/common/typing.py +65 -44
  91. flwr/compat/client/app.py +45 -47
  92. flwr/compat/client/grpc_client/connection.py +15 -14
  93. flwr/compat/common/constant.py +29 -0
  94. flwr/compat/server/app.py +6 -7
  95. flwr/proto/appio_pb2.py +13 -3
  96. flwr/proto/appio_pb2.pyi +134 -65
  97. flwr/proto/appio_pb2_grpc.py +20 -0
  98. flwr/proto/appio_pb2_grpc.pyi +27 -0
  99. flwr/proto/clientappio_pb2.py +17 -7
  100. flwr/proto/clientappio_pb2.pyi +15 -0
  101. flwr/proto/clientappio_pb2_grpc.py +206 -40
  102. flwr/proto/clientappio_pb2_grpc.pyi +168 -53
  103. flwr/proto/control_pb2.py +72 -40
  104. flwr/proto/control_pb2.pyi +319 -87
  105. flwr/proto/control_pb2_grpc.py +339 -28
  106. flwr/proto/control_pb2_grpc.pyi +209 -37
  107. flwr/proto/error_pb2.py +13 -3
  108. flwr/proto/error_pb2.pyi +24 -6
  109. flwr/proto/error_pb2_grpc.py +20 -0
  110. flwr/proto/error_pb2_grpc.pyi +27 -0
  111. flwr/proto/fab_pb2.py +24 -10
  112. flwr/proto/fab_pb2.pyi +68 -20
  113. flwr/proto/fab_pb2_grpc.py +20 -0
  114. flwr/proto/fab_pb2_grpc.pyi +27 -0
  115. flwr/proto/federation_pb2.py +38 -0
  116. flwr/proto/federation_pb2.pyi +56 -0
  117. flwr/proto/federation_pb2_grpc.py +24 -0
  118. flwr/proto/federation_pb2_grpc.pyi +31 -0
  119. flwr/proto/fleet_pb2.py +45 -27
  120. flwr/proto/fleet_pb2.pyi +190 -70
  121. flwr/proto/fleet_pb2_grpc.py +277 -66
  122. flwr/proto/fleet_pb2_grpc.pyi +201 -55
  123. flwr/proto/grpcadapter_pb2.py +14 -4
  124. flwr/proto/grpcadapter_pb2.pyi +38 -16
  125. flwr/proto/grpcadapter_pb2_grpc.py +35 -4
  126. flwr/proto/grpcadapter_pb2_grpc.pyi +38 -7
  127. flwr/proto/heartbeat_pb2.py +17 -7
  128. flwr/proto/heartbeat_pb2.pyi +51 -22
  129. flwr/proto/heartbeat_pb2_grpc.py +20 -0
  130. flwr/proto/heartbeat_pb2_grpc.pyi +27 -0
  131. flwr/proto/log_pb2.py +13 -3
  132. flwr/proto/log_pb2.pyi +34 -11
  133. flwr/proto/log_pb2_grpc.py +20 -0
  134. flwr/proto/log_pb2_grpc.pyi +27 -0
  135. flwr/proto/message_pb2.py +15 -5
  136. flwr/proto/message_pb2.pyi +154 -86
  137. flwr/proto/message_pb2_grpc.py +20 -0
  138. flwr/proto/message_pb2_grpc.pyi +27 -0
  139. flwr/proto/node_pb2.py +16 -4
  140. flwr/proto/node_pb2.pyi +77 -4
  141. flwr/proto/node_pb2_grpc.py +20 -0
  142. flwr/proto/node_pb2_grpc.pyi +27 -0
  143. flwr/proto/recorddict_pb2.py +13 -3
  144. flwr/proto/recorddict_pb2.pyi +184 -107
  145. flwr/proto/recorddict_pb2_grpc.py +20 -0
  146. flwr/proto/recorddict_pb2_grpc.pyi +27 -0
  147. flwr/proto/run_pb2.py +40 -31
  148. flwr/proto/run_pb2.pyi +158 -84
  149. flwr/proto/run_pb2_grpc.py +20 -0
  150. flwr/proto/run_pb2_grpc.pyi +27 -0
  151. flwr/proto/serverappio_pb2.py +13 -3
  152. flwr/proto/serverappio_pb2.pyi +32 -8
  153. flwr/proto/serverappio_pb2_grpc.py +246 -65
  154. flwr/proto/serverappio_pb2_grpc.pyi +221 -85
  155. flwr/proto/simulationio_pb2.py +16 -8
  156. flwr/proto/simulationio_pb2.pyi +15 -0
  157. flwr/proto/simulationio_pb2_grpc.py +162 -41
  158. flwr/proto/simulationio_pb2_grpc.pyi +149 -55
  159. flwr/proto/transport_pb2.py +20 -10
  160. flwr/proto/transport_pb2.pyi +249 -160
  161. flwr/proto/transport_pb2_grpc.py +35 -4
  162. flwr/proto/transport_pb2_grpc.pyi +38 -8
  163. flwr/server/app.py +175 -128
  164. flwr/server/client_manager.py +4 -5
  165. flwr/server/client_proxy.py +10 -11
  166. flwr/server/compat/app.py +4 -5
  167. flwr/server/compat/app_utils.py +2 -1
  168. flwr/server/compat/grid_client_proxy.py +12 -13
  169. flwr/server/compat/legacy_context.py +3 -4
  170. flwr/server/fleet_event_log_interceptor.py +2 -1
  171. flwr/server/grid/grid.py +2 -3
  172. flwr/server/grid/grpc_grid.py +12 -10
  173. flwr/server/grid/inmemory_grid.py +4 -4
  174. flwr/server/run_serverapp.py +2 -3
  175. flwr/server/server.py +34 -39
  176. flwr/server/server_app.py +7 -8
  177. flwr/server/server_config.py +1 -2
  178. flwr/server/serverapp/app.py +34 -28
  179. flwr/server/serverapp_components.py +4 -5
  180. flwr/server/strategy/aggregate.py +9 -8
  181. flwr/server/strategy/bulyan.py +13 -11
  182. flwr/server/strategy/dp_adaptive_clipping.py +16 -20
  183. flwr/server/strategy/dp_fixed_clipping.py +12 -17
  184. flwr/server/strategy/dpfedavg_adaptive.py +3 -4
  185. flwr/server/strategy/dpfedavg_fixed.py +6 -10
  186. flwr/server/strategy/fault_tolerant_fedavg.py +14 -13
  187. flwr/server/strategy/fedadagrad.py +18 -14
  188. flwr/server/strategy/fedadam.py +16 -14
  189. flwr/server/strategy/fedavg.py +16 -17
  190. flwr/server/strategy/fedavg_android.py +15 -15
  191. flwr/server/strategy/fedavgm.py +21 -18
  192. flwr/server/strategy/fedmedian.py +2 -3
  193. flwr/server/strategy/fedopt.py +11 -10
  194. flwr/server/strategy/fedprox.py +10 -9
  195. flwr/server/strategy/fedtrimmedavg.py +12 -11
  196. flwr/server/strategy/fedxgb_bagging.py +13 -11
  197. flwr/server/strategy/fedxgb_cyclic.py +6 -6
  198. flwr/server/strategy/fedxgb_nn_avg.py +4 -4
  199. flwr/server/strategy/fedyogi.py +16 -14
  200. flwr/server/strategy/krum.py +12 -11
  201. flwr/server/strategy/qfedavg.py +16 -15
  202. flwr/server/strategy/strategy.py +6 -9
  203. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +20 -9
  204. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -2
  205. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -4
  206. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +10 -12
  207. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +1 -3
  208. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +136 -42
  209. flwr/server/superlink/fleet/grpc_rere/{server_interceptor.py → node_auth_server_interceptor.py} +28 -50
  210. flwr/server/superlink/fleet/message_handler/message_handler.py +141 -51
  211. flwr/server/superlink/fleet/rest_rere/rest_api.py +54 -33
  212. flwr/server/superlink/fleet/vce/backend/backend.py +2 -2
  213. flwr/server/superlink/fleet/vce/backend/raybackend.py +6 -6
  214. flwr/server/superlink/fleet/vce/vce_api.py +32 -13
  215. flwr/server/superlink/linkstate/__init__.py +2 -0
  216. flwr/server/superlink/linkstate/in_memory_linkstate.py +293 -208
  217. flwr/server/superlink/linkstate/linkstate.py +176 -64
  218. flwr/server/superlink/linkstate/linkstate_factory.py +24 -6
  219. flwr/server/superlink/linkstate/sql_linkstate.py +221 -0
  220. flwr/server/superlink/linkstate/sqlite_linkstate.py +743 -648
  221. flwr/server/superlink/linkstate/utils.py +11 -62
  222. flwr/server/superlink/serverappio/serverappio_grpc.py +1 -2
  223. flwr/server/superlink/serverappio/serverappio_servicer.py +28 -23
  224. flwr/server/superlink/simulation/simulationio_grpc.py +1 -2
  225. flwr/server/superlink/simulation/simulationio_servicer.py +19 -14
  226. flwr/server/superlink/utils.py +4 -6
  227. flwr/server/typing.py +1 -1
  228. flwr/server/utils/tensorboard.py +15 -8
  229. flwr/server/utils/validator.py +2 -3
  230. flwr/server/workflow/default_workflows.py +7 -6
  231. flwr/server/workflow/secure_aggregation/secagg_workflow.py +2 -4
  232. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +13 -11
  233. flwr/serverapp/strategy/bulyan.py +16 -15
  234. flwr/serverapp/strategy/dp_adaptive_clipping.py +12 -11
  235. flwr/serverapp/strategy/dp_fixed_clipping.py +11 -14
  236. flwr/serverapp/strategy/fedadagrad.py +10 -11
  237. flwr/serverapp/strategy/fedadam.py +10 -11
  238. flwr/serverapp/strategy/fedavg.py +10 -11
  239. flwr/serverapp/strategy/fedavgm.py +17 -16
  240. flwr/serverapp/strategy/fedmedian.py +2 -2
  241. flwr/serverapp/strategy/fedopt.py +10 -11
  242. flwr/serverapp/strategy/fedprox.py +7 -8
  243. flwr/serverapp/strategy/fedtrimmedavg.py +9 -9
  244. flwr/serverapp/strategy/fedxgb_bagging.py +3 -3
  245. flwr/serverapp/strategy/fedxgb_cyclic.py +10 -10
  246. flwr/serverapp/strategy/fedyogi.py +9 -11
  247. flwr/serverapp/strategy/krum.py +7 -7
  248. flwr/serverapp/strategy/multikrum.py +9 -9
  249. flwr/serverapp/strategy/qfedavg.py +17 -16
  250. flwr/serverapp/strategy/strategy.py +6 -9
  251. flwr/serverapp/strategy/strategy_utils.py +7 -8
  252. flwr/simulation/app.py +46 -42
  253. flwr/simulation/legacy_app.py +12 -12
  254. flwr/simulation/ray_transport/ray_actor.py +11 -12
  255. flwr/simulation/ray_transport/ray_client_proxy.py +14 -19
  256. flwr/simulation/run_simulation.py +46 -44
  257. flwr/simulation/simulationio_connection.py +4 -4
  258. flwr/{common → supercore}/address.py +1 -37
  259. flwr/supercore/cli/flower_superexec.py +3 -4
  260. flwr/supercore/constant.py +69 -0
  261. flwr/supercore/corestate/corestate.py +24 -3
  262. flwr/supercore/corestate/in_memory_corestate.py +138 -0
  263. flwr/supercore/corestate/sql_corestate.py +153 -0
  264. flwr/supercore/corestate/sqlite_corestate.py +157 -0
  265. flwr/supercore/credential_store/__init__.py +33 -0
  266. flwr/supercore/credential_store/credential_store.py +34 -0
  267. flwr/supercore/credential_store/file_credential_store.py +76 -0
  268. flwr/{common → supercore}/date.py +0 -11
  269. flwr/supercore/ffs/disk_ffs.py +1 -2
  270. flwr/supercore/ffs/ffs.py +1 -2
  271. flwr/supercore/ffs/ffs_factory.py +1 -2
  272. flwr/{common → supercore}/heartbeat.py +20 -25
  273. flwr/supercore/object_store/in_memory_object_store.py +1 -6
  274. flwr/supercore/object_store/object_store.py +1 -2
  275. flwr/supercore/object_store/object_store_factory.py +27 -8
  276. flwr/supercore/object_store/sqlite_object_store.py +253 -0
  277. flwr/{cli/new/templates/app → supercore/primitives}/__init__.py +1 -1
  278. flwr/supercore/primitives/asymmetric.py +117 -0
  279. flwr/supercore/primitives/asymmetric_ed25519.py +175 -0
  280. flwr/supercore/sql_mixin.py +292 -0
  281. flwr/supercore/sqlite_mixin.py +156 -0
  282. flwr/{client/clientapp → supercore/state}/__init__.py +2 -2
  283. flwr/supercore/state/schema/README.md +125 -0
  284. flwr/{cli/new/templates → supercore/state/schema}/__init__.py +2 -2
  285. flwr/supercore/state/schema/corestate_tables.py +36 -0
  286. flwr/supercore/state/schema/linkstate_tables.py +152 -0
  287. flwr/supercore/state/schema/objectstore_tables.py +90 -0
  288. flwr/supercore/superexec/plugin/base_exec_plugin.py +1 -2
  289. flwr/supercore/superexec/plugin/exec_plugin.py +3 -3
  290. flwr/supercore/superexec/run_superexec.py +9 -13
  291. flwr/supercore/utils.py +224 -0
  292. flwr/superlink/artifact_provider/artifact_provider.py +1 -2
  293. flwr/superlink/auth_plugin/__init__.py +5 -2
  294. flwr/superlink/auth_plugin/auth_plugin.py +20 -19
  295. flwr/superlink/auth_plugin/noop_auth_plugin.py +84 -0
  296. flwr/superlink/federation/__init__.py +24 -0
  297. flwr/superlink/federation/federation_manager.py +64 -0
  298. flwr/superlink/federation/noop_federation_manager.py +71 -0
  299. flwr/superlink/servicer/control/{control_user_auth_interceptor.py → control_account_auth_interceptor.py} +41 -32
  300. flwr/superlink/servicer/control/control_event_log_interceptor.py +7 -7
  301. flwr/superlink/servicer/control/control_grpc.py +20 -17
  302. flwr/superlink/servicer/control/control_license_interceptor.py +3 -3
  303. flwr/superlink/servicer/control/control_servicer.py +328 -68
  304. flwr/supernode/cli/flower_supernode.py +74 -26
  305. flwr/supernode/nodestate/in_memory_nodestate.py +121 -49
  306. flwr/supernode/nodestate/nodestate.py +52 -8
  307. flwr/supernode/nodestate/nodestate_factory.py +7 -4
  308. flwr/supernode/runtime/run_clientapp.py +43 -24
  309. flwr/supernode/servicer/clientappio/clientappio_servicer.py +48 -10
  310. flwr/supernode/start_client_internal.py +185 -57
  311. {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/METADATA +10 -11
  312. flwr_nightly-1.26.0.dev20260121.dist-info/RECORD +411 -0
  313. flwr/cli/new/templates/app/.gitignore.tpl +0 -163
  314. flwr/cli/new/templates/app/LICENSE.tpl +0 -202
  315. flwr/cli/new/templates/app/README.baseline.md.tpl +0 -127
  316. flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -68
  317. flwr/cli/new/templates/app/README.md.tpl +0 -37
  318. flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -1
  319. flwr/cli/new/templates/app/code/__init__.py.tpl +0 -1
  320. flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +0 -1
  321. flwr/cli/new/templates/app/code/client.baseline.py.tpl +0 -75
  322. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -93
  323. flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -71
  324. flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -102
  325. flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -46
  326. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -80
  327. flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +0 -55
  328. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -108
  329. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -82
  330. flwr/cli/new/templates/app/code/client.xgboost.py.tpl +0 -110
  331. flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -36
  332. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -92
  333. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -87
  334. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -56
  335. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -73
  336. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -78
  337. flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -66
  338. flwr/cli/new/templates/app/code/server.baseline.py.tpl +0 -43
  339. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -42
  340. flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -39
  341. flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -41
  342. flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -38
  343. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -41
  344. flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +0 -31
  345. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -44
  346. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -38
  347. flwr/cli/new/templates/app/code/server.xgboost.py.tpl +0 -56
  348. flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -1
  349. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -98
  350. flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -57
  351. flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -102
  352. flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -7
  353. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -98
  354. flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +0 -111
  355. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -67
  356. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -52
  357. flwr/cli/new/templates/app/code/task.xgboost.py.tpl +0 -67
  358. flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -1
  359. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +0 -146
  360. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +0 -80
  361. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +0 -65
  362. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +0 -52
  363. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +0 -56
  364. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -49
  365. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -53
  366. flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +0 -53
  367. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +0 -52
  368. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -53
  369. flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +0 -61
  370. flwr/common/pyproject.py +0 -42
  371. flwr/supercore/object_store/utils.py +0 -43
  372. flwr_nightly-1.23.0.dev20250930.dist-info/RECORD +0 -429
  373. /flwr/{common → supercore}/version.py +0 -0
  374. {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/WHEEL +0 -0
  375. {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/entry_points.txt +0 -0
flwr/common/config.py CHANGED
@@ -20,11 +20,12 @@ import re
20
20
  import zipfile
21
21
  from io import BytesIO
22
22
  from pathlib import Path
23
- from typing import IO, Any, Optional, TypeVar, Union, cast, get_args
23
+ from typing import IO, Any, TypeVar, cast, get_args
24
24
 
25
25
  import tomli
26
26
  import typer
27
27
 
28
+ from flwr.app.user_config import UserConfig, UserConfigValue
28
29
  from flwr.common.constant import (
29
30
  APP_DIR,
30
31
  FAB_CONFIG_FILE,
@@ -32,14 +33,14 @@ from flwr.common.constant import (
32
33
  FLWR_DIR,
33
34
  FLWR_HOME,
34
35
  )
35
- from flwr.common.typing import Run, UserConfig, UserConfigValue
36
+ from flwr.common.typing import Run
36
37
 
37
38
  from . import ConfigRecord, object_ref
38
39
 
39
40
  T_dict = TypeVar("T_dict", bound=dict[str, Any]) # pylint: disable=invalid-name
40
41
 
41
42
 
42
- def get_flwr_dir(provided_path: Optional[str] = None) -> Path:
43
+ def get_flwr_dir(provided_path: str | None = None) -> Path:
43
44
  """Return the Flower home directory based on env variables."""
44
45
  if provided_path is None or not Path(provided_path).is_dir():
45
46
  return Path(
@@ -55,7 +56,7 @@ def get_project_dir(
55
56
  fab_id: str,
56
57
  fab_version: str,
57
58
  fab_hash: str,
58
- flwr_dir: Optional[Union[str, Path]] = None,
59
+ flwr_dir: str | Path | None = None,
59
60
  ) -> Path:
60
61
  """Return the project directory based on the given fab_id and fab_version."""
61
62
  # Check the fab_id
@@ -73,7 +74,7 @@ def get_project_dir(
73
74
  )
74
75
 
75
76
 
76
- def get_project_config(project_dir: Union[str, Path]) -> dict[str, Any]:
77
+ def get_project_config(project_dir: str | Path) -> dict[str, Any]:
77
78
  """Return pyproject.toml in the given project directory."""
78
79
  # Load pyproject.toml file
79
80
  toml_path = Path(project_dir) / FAB_CONFIG_FILE
@@ -134,7 +135,7 @@ def get_fused_config_from_dir(
134
135
  return fuse_dicts(flat_default_config, override_config)
135
136
 
136
137
 
137
- def get_fused_config_from_fab(fab_file: Union[Path, bytes], run: Run) -> UserConfig:
138
+ def get_fused_config_from_fab(fab_file: Path | bytes, run: Run) -> UserConfig:
138
139
  """Fuse default config in a `FAB` with overrides in a `Run`.
139
140
 
140
141
  This enables obtaining a run-config without having to install the FAB. This
@@ -146,7 +147,7 @@ def get_fused_config_from_fab(fab_file: Union[Path, bytes], run: Run) -> UserCon
146
147
  return fuse_dicts(flat_config_flat, run.override_config)
147
148
 
148
149
 
149
- def get_fused_config(run: Run, flwr_dir: Optional[Path]) -> UserConfig:
150
+ def get_fused_config(run: Run, flwr_dir: Path | None) -> UserConfig:
150
151
  """Merge the overrides from a `Run` with the config from a FAB.
151
152
 
152
153
  Get the config using the fab_id and the fab_version, remove the nesting by adding
@@ -165,9 +166,7 @@ def get_fused_config(run: Run, flwr_dir: Optional[Path]) -> UserConfig:
165
166
  return get_fused_config_from_dir(project_dir, run.override_config)
166
167
 
167
168
 
168
- def flatten_dict(
169
- raw_dict: Optional[dict[str, Any]], parent_key: str = ""
170
- ) -> UserConfig:
169
+ def flatten_dict(raw_dict: dict[str, Any] | None, parent_key: str = "") -> UserConfig:
171
170
  """Flatten dict by joining nested keys with a given separator."""
172
171
  if raw_dict is None:
173
172
  return {}
@@ -205,9 +204,7 @@ def unflatten_dict(flat_dict: dict[str, Any]) -> dict[str, Any]:
205
204
  return unflattened_dict
206
205
 
207
206
 
208
- def parse_config_args(
209
- config: Optional[list[str]], flatten: bool = True
210
- ) -> dict[str, Any]:
207
+ def parse_config_args(config: list[str] | None, flatten: bool = True) -> dict[str, Any]:
211
208
  """Parse separator separated list of key-value pairs separated by '='."""
212
209
  overrides: UserConfig = {}
213
210
 
@@ -246,6 +243,7 @@ def parse_config_args(
246
243
  "space-separated key-value pairs.",
247
244
  fg=typer.colors.RED,
248
245
  bold=True,
246
+ err=True,
249
247
  )
250
248
  raise typer.Exit(code=1) from err
251
249
 
@@ -269,7 +267,7 @@ def user_config_to_configrecord(config: UserConfig) -> ConfigRecord:
269
267
  return c_record
270
268
 
271
269
 
272
- def get_fab_config(fab_file: Union[Path, bytes]) -> dict[str, Any]:
270
+ def get_fab_config(fab_file: Path | bytes) -> dict[str, Any]:
273
271
  """Extract the config from a FAB file or path.
274
272
 
275
273
  Parameters
@@ -283,7 +281,7 @@ def get_fab_config(fab_file: Union[Path, bytes]) -> dict[str, Any]:
283
281
  Dict[str, Any]
284
282
  The `config` of the given Flower App Bundle.
285
283
  """
286
- fab_file_archive: Union[Path, IO[bytes]]
284
+ fab_file_archive: Path | IO[bytes]
287
285
  if isinstance(fab_file, bytes):
288
286
  fab_file_archive = BytesIO(fab_file)
289
287
  elif isinstance(fab_file, Path):
@@ -319,7 +317,7 @@ def _validate_run_config(config_dict: dict[str, Any], errors: list[str]) -> None
319
317
 
320
318
  # pylint: disable=too-many-branches
321
319
  def validate_fields_in_config(
322
- config: dict[str, Any]
320
+ config: dict[str, Any],
323
321
  ) -> tuple[bool, list[str], list[str]]:
324
322
  """Validate pyproject.toml fields."""
325
323
  errors = []
@@ -368,7 +366,7 @@ def validate_fields_in_config(
368
366
  def validate_config(
369
367
  config: dict[str, Any],
370
368
  check_module: bool = True,
371
- project_dir: Optional[Union[str, Path]] = None,
369
+ project_dir: str | Path | None = None,
372
370
  ) -> tuple[bool, list[str], list[str]]:
373
371
  """Validate pyproject.toml."""
374
372
  is_valid, errors, warnings = validate_fields_in_config(config)
flwr/common/constant.py CHANGED
@@ -17,13 +17,13 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- TRANSPORT_TYPE_GRPC_BIDI = "grpc-bidi"
20
+ import os
21
+
21
22
  TRANSPORT_TYPE_GRPC_RERE = "grpc-rere"
22
23
  TRANSPORT_TYPE_GRPC_ADAPTER = "grpc-adapter"
23
24
  TRANSPORT_TYPE_REST = "rest"
24
25
  TRANSPORT_TYPE_VCE = "vce"
25
26
  TRANSPORT_TYPES = [
26
- TRANSPORT_TYPE_GRPC_BIDI,
27
27
  TRANSPORT_TYPE_GRPC_RERE,
28
28
  TRANSPORT_TYPE_REST,
29
29
  TRANSPORT_TYPE_VCE,
@@ -60,7 +60,9 @@ HEARTBEAT_DEFAULT_INTERVAL = 30
60
60
  HEARTBEAT_CALL_TIMEOUT = 5
61
61
  HEARTBEAT_BASE_MULTIPLIER = 0.8
62
62
  HEARTBEAT_RANDOM_RANGE = (-0.1, 0.1)
63
- HEARTBEAT_MAX_INTERVAL = 1e300
63
+ HEARTBEAT_MIN_INTERVAL = 10
64
+ HEARTBEAT_MAX_INTERVAL = 1800 # 30 minutes
65
+ HEARTBEAT_INTERVAL_INF = 1e300 # Large value, disabling heartbeats
64
66
  HEARTBEAT_PATIENCE = 2
65
67
  RUN_FAILURE_DETAILS_NO_HEARTBEAT = "No heartbeat received from the run."
66
68
 
@@ -70,13 +72,24 @@ NODE_ID_NUM_BYTES = 8
70
72
 
71
73
  # Constants for FAB
72
74
  APP_DIR = "apps"
73
- FAB_ALLOWED_EXTENSIONS = {".py", ".toml", ".md"}
74
75
  FAB_CONFIG_FILE = "pyproject.toml"
75
76
  FAB_DATE = (2024, 10, 1, 0, 0, 0)
76
77
  FAB_HASH_TRUNCATION = 8
77
78
  FAB_MAX_SIZE = 10 * 1024 * 1024 # 10 MB
78
79
  FLWR_DIR = ".flwr" # The default Flower directory: ~/.flwr/
79
80
  FLWR_HOME = "FLWR_HOME" # If set, override the default Flower directory
81
+ # FAB file include patterns (gitignore-style patterns)
82
+ FAB_INCLUDE_PATTERNS = (
83
+ "**/*.py",
84
+ "**/*.toml",
85
+ "**/*.md",
86
+ )
87
+ # FAB file exclude patterns (gitignore-style patterns)
88
+ FAB_EXCLUDE_PATTERNS = (
89
+ f"{FLWR_DIR}/**", # Exclude the .flwr directory
90
+ "**/__pycache__/**",
91
+ FAB_CONFIG_FILE, # Exclude the original pyproject.toml
92
+ )
80
93
 
81
94
  # Constant for SuperLink
82
95
  SUPERLINK_NODE_ID = 1
@@ -109,14 +122,14 @@ LOG_UPLOAD_INTERVAL = 0.2 # Minimum interval between two log uploads
109
122
  # Retry configurations
110
123
  MAX_RETRY_DELAY = 20 # Maximum delay duration between two consecutive retries.
111
124
 
112
- # Constants for user authentication
125
+ # Constants for account authentication
113
126
  CREDENTIALS_DIR = ".credentials"
114
- AUTH_TYPE_JSON_KEY = "auth-type" # For key name in JSON file
115
- AUTH_TYPE_YAML_KEY = "auth_type" # For key name in YAML file
127
+ AUTHN_TYPE_JSON_KEY = "authn-type" # For key name in JSON file
128
+ AUTHN_TYPE_YAML_KEY = "authn_type" # For key name in YAML file
116
129
  ACCESS_TOKEN_KEY = "flwr-oidc-access-token"
117
130
  REFRESH_TOKEN_KEY = "flwr-oidc-refresh-token"
118
131
 
119
- # Constants for user authorization
132
+ # Constants for account authorization
120
133
  AUTHZ_TYPE_YAML_KEY = "authz_type" # For key name in YAML file
121
134
 
122
135
  # Constants for node authentication
@@ -135,7 +148,9 @@ GC_THRESHOLD = 200_000_000 # 200 MB
135
148
  # Constants for Inflatable
136
149
  HEAD_BODY_DIVIDER = b"\x00"
137
150
  HEAD_VALUE_DIVIDER = " "
138
- MAX_ARRAY_CHUNK_SIZE = 20_971_520 # 20 MB
151
+ FLWR_PRIVATE_MAX_ARRAY_CHUNK_SIZE = int(
152
+ os.getenv("FLWR_PRIVATE_MAX_ARRAY_CHUNK_SIZE", "5242880")
153
+ ) # 5 MB
139
154
 
140
155
  # Constants for serialization
141
156
  INT64_MAX_VALUE = 9223372036854775807 # (1 << 63) - 1
@@ -144,8 +159,12 @@ INT64_MAX_VALUE = 9223372036854775807 # (1 << 63) - 1
144
159
  FLWR_APP_TOKEN_LENGTH = 128 # Length of the token used
145
160
 
146
161
  # Constants for object pushing and pulling
147
- MAX_CONCURRENT_PUSHES = 8 # Default maximum number of concurrent pushes
148
- MAX_CONCURRENT_PULLS = 8 # Default maximum number of concurrent pulls
162
+ FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PUSHES = int(
163
+ os.getenv("FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PUSHES", "2")
164
+ ) # Default maximum number of concurrent pushes
165
+ FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PULLS = int(
166
+ os.getenv("FLWR_PRIVATE_MAX_CONCURRENT_OBJ_PULLS", "2")
167
+ ) # Default maximum number of concurrent pulls
149
168
  PULL_MAX_TIME = 7200 # Default maximum time to wait for pulling objects
150
169
  PULL_MAX_TRIES_PER_OBJECT = 500 # Default maximum number of tries to pull an object
151
170
  PULL_INITIAL_BACKOFF = 1 # Initial backoff time for pulling objects
@@ -154,22 +173,13 @@ PULL_BACKOFF_CAP = 10 # Maximum backoff time for pulling objects
154
173
 
155
174
  # ControlServicer constants
156
175
  RUN_ID_NOT_FOUND_MESSAGE = "Run ID not found"
157
- NO_USER_AUTH_MESSAGE = "ControlServicer initialized without user authentication"
176
+ NO_ACCOUNT_AUTH_MESSAGE = "ControlServicer initialized without account authentication"
158
177
  NO_ARTIFACT_PROVIDER_MESSAGE = "ControlServicer initialized without artifact provider"
159
178
  PULL_UNFINISHED_RUN_MESSAGE = "Cannot pull artifacts for an unfinished run"
160
-
161
-
162
- class MessageType:
163
- """Message type."""
164
-
165
- TRAIN = "train"
166
- EVALUATE = "evaluate"
167
- QUERY = "query"
168
- SYSTEM = "system"
169
-
170
- def __new__(cls) -> MessageType:
171
- """Prevent instantiation."""
172
- raise TypeError(f"{cls.__name__} cannot be instantiated.")
179
+ SUPERNODE_NOT_CREATED_FROM_CLI_MESSAGE = "Invalid SuperNode credentials"
180
+ PUBLIC_KEY_ALREADY_IN_USE_MESSAGE = "Public key already in use"
181
+ PUBLIC_KEY_NOT_VALID = "The provided public key is not valid"
182
+ NODE_NOT_FOUND_MESSAGE = "Node ID not found for account"
173
183
 
174
184
 
175
185
  class MessageTypeLegacy:
@@ -203,6 +213,8 @@ class ErrorCode:
203
213
  REPLY_MESSAGE_UNAVAILABLE = 4
204
214
  NODE_UNAVAILABLE = 5
205
215
  MOD_FAILED_PRECONDITION = 6
216
+ INVALID_FAB = 7
217
+ CLIENT_APP_CRASHED = 8
206
218
 
207
219
  def __new__(cls) -> ErrorCode:
208
220
  """Prevent instantiation."""
@@ -245,12 +257,23 @@ class CliOutputFormat:
245
257
  raise TypeError(f"{cls.__name__} cannot be instantiated.")
246
258
 
247
259
 
248
- class AuthType:
249
- """User authentication types."""
260
+ class AuthnType:
261
+ """Account authentication types."""
250
262
 
263
+ NOOP = "noop"
251
264
  OIDC = "oidc"
252
265
 
253
- def __new__(cls) -> AuthType:
266
+ def __new__(cls) -> AuthnType:
267
+ """Prevent instantiation."""
268
+ raise TypeError(f"{cls.__name__} cannot be instantiated.")
269
+
270
+
271
+ class AuthzType:
272
+ """Account authorization types."""
273
+
274
+ NOOP = "noop"
275
+
276
+ def __new__(cls) -> AuthzType:
254
277
  """Prevent instantiation."""
255
278
  raise TypeError(f"{cls.__name__} cannot be instantiated.")
256
279
 
@@ -281,3 +304,8 @@ class ExecPluginType:
281
304
  """Return all SuperExec plugin types."""
282
305
  # Filter all constants (uppercase) of the class
283
306
  return [v for k, v in vars(ExecPluginType).items() if k.isupper()]
307
+
308
+
309
+ # Constants for No-op auth plugins
310
+ NOOP_FLWR_AID = "<id:none>"
311
+ NOOP_ACCOUNT_NAME = "<name:none>"
flwr/common/context.py CHANGED
@@ -17,8 +17,9 @@
17
17
 
18
18
  from dataclasses import dataclass
19
19
 
20
+ from flwr.app.user_config import UserConfig
21
+
20
22
  from .record import RecordDict
21
- from .typing import UserConfig
22
23
 
23
24
 
24
25
  @dataclass
@@ -16,7 +16,6 @@
16
16
 
17
17
 
18
18
  from logging import WARNING
19
- from typing import Optional
20
19
 
21
20
  import numpy as np
22
21
 
@@ -70,7 +69,7 @@ def compute_clip_model_update(
70
69
  """Compute model update (param1 - param2) and clip it.
71
70
 
72
71
  Then add the clipped value to param1."""
73
- model_update = [np.subtract(x, y) for (x, y) in zip(param1, param2)]
72
+ model_update = [np.subtract(x, y) for (x, y) in zip(param1, param2, strict=True)]
74
73
  clip_inputs_inplace(model_update, clipping_norm)
75
74
 
76
75
  for i, _ in enumerate(param2):
@@ -98,7 +97,7 @@ def compute_adaptive_clip_model_update(
98
97
  model update = param1 - param2
99
98
  Return the norm_bit
100
99
  """
101
- model_update = [np.subtract(x, y) for (x, y) in zip(param1, param2)]
100
+ model_update = [np.subtract(x, y) for (x, y) in zip(param1, param2, strict=True)]
102
101
  norm_bit = adaptive_clip_inputs_inplace(model_update, clipping_norm)
103
102
 
104
103
  for i, _ in enumerate(param2):
@@ -125,7 +124,7 @@ def add_gaussian_noise_to_params(
125
124
  def compute_adaptive_noise_params(
126
125
  noise_multiplier: float,
127
126
  num_sampled_clients: float,
128
- clipped_count_stddev: Optional[float],
127
+ clipped_count_stddev: float | None,
129
128
  ) -> tuple[float, float]:
130
129
  """Compute noising parameters for the adaptive clipping.
131
130
 
@@ -16,7 +16,6 @@
16
16
 
17
17
 
18
18
  from abc import ABC, abstractmethod
19
- from typing import Optional, Union
20
19
 
21
20
  import grpc
22
21
  from google.protobuf.message import Message as GrpcMessage
@@ -36,7 +35,7 @@ class EventLogWriterPlugin(ABC):
36
35
  self,
37
36
  request: GrpcMessage,
38
37
  context: grpc.ServicerContext,
39
- account_info: Optional[AccountInfo],
38
+ account_info: AccountInfo | None,
40
39
  method_name: str,
41
40
  ) -> LogEntry:
42
41
  """Compose pre-event log entry from the provided request and context."""
@@ -46,9 +45,9 @@ class EventLogWriterPlugin(ABC):
46
45
  self,
47
46
  request: GrpcMessage,
48
47
  context: grpc.ServicerContext,
49
- account_info: Optional[AccountInfo],
48
+ account_info: AccountInfo | None,
50
49
  method_name: str,
51
- response: Optional[Union[GrpcMessage, BaseException]],
50
+ response: GrpcMessage | BaseException | None,
52
51
  ) -> LogEntry:
53
52
  """Compose post-event log entry from the provided response and context."""
54
53
 
flwr/common/exit/exit.py CHANGED
@@ -15,14 +15,16 @@
15
15
  """Unified exit function."""
16
16
 
17
17
 
18
- from __future__ import annotations
19
-
18
+ import os
20
19
  import sys
20
+ import threading
21
+ import time
21
22
  from logging import ERROR, INFO
22
23
  from typing import Any, NoReturn
23
24
 
24
25
  from flwr.common import EventType, event
25
- from flwr.common.version import package_version
26
+ from flwr.supercore.constant import FORCE_EXIT_TIMEOUT_SECONDS
27
+ from flwr.supercore.version import package_version
26
28
 
27
29
  from ..logger import log
28
30
  from .exit_code import EXIT_CODE_HELP
@@ -53,6 +55,10 @@ def flwr_exit(
53
55
  - `<message>`: Optional context or additional information about the exit.
54
56
  - `<short-help-message>`: A brief explanation for the given exit code.
55
57
  - `<help-page-url>`: A URL providing detailed documentation and resolution steps.
58
+
59
+ Notes
60
+ -----
61
+ This function MUST be called from the main thread.
56
62
  """
57
63
  is_error = not 0 <= code < 100 # 0-99 are success exit codes
58
64
 
@@ -84,6 +90,13 @@ def flwr_exit(
84
90
  # Trigger exit handlers
85
91
  trigger_exit_handlers()
86
92
 
93
+ # Start a daemon thread to force exit if graceful exit fails
94
+ def force_exit() -> None:
95
+ time.sleep(FORCE_EXIT_TIMEOUT_SECONDS)
96
+ os._exit(sys_exit_code)
97
+
98
+ threading.Thread(target=force_exit, daemon=True).start()
99
+
87
100
  # Exit
88
101
  sys.exit(sys_exit_code)
89
102
 
@@ -38,20 +38,29 @@ class ExitCode:
38
38
  SERVERAPP_STRATEGY_PRECONDITION_UNMET = 200
39
39
  SERVERAPP_EXCEPTION = 201
40
40
  SERVERAPP_STRATEGY_AGGREGATION_ERROR = 202
41
+ SERVERAPP_RUN_START_REJECTED = 203
41
42
 
42
43
  # SuperNode-specific exit codes (300-399)
43
44
  SUPERNODE_REST_ADDRESS_INVALID = 300
44
- SUPERNODE_NODE_AUTH_KEYS_REQUIRED = 301
45
- SUPERNODE_NODE_AUTH_KEYS_INVALID = 302
45
+ # SUPERNODE_NODE_AUTH_KEYS_REQUIRED = 301 --- DELETED ---
46
+ SUPERNODE_NODE_AUTH_KEY_INVALID = 302
47
+ SUPERNODE_STARTED_WITHOUT_TLS_BUT_NODE_AUTH_ENABLED = 303
48
+ SUPERNODE_INVALID_TRUSTED_ENTITIES = 304
46
49
 
47
50
  # SuperExec-specific exit codes (400-499)
48
51
  SUPEREXEC_INVALID_PLUGIN_CONFIG = 400
49
52
 
53
+ # FlowerCLI-specific exit codes (500-599)
54
+ FLWRCLI_NODE_AUTH_PUBLIC_KEY_INVALID = 500
55
+
50
56
  # Common exit codes (600-699)
51
57
  COMMON_ADDRESS_INVALID = 600
52
58
  COMMON_MISSING_EXTRA_REST = 601
53
59
  COMMON_TLS_NOT_SUPPORTED = 602
54
60
 
61
+ # Simulation exit codes (700-799)
62
+ SIMULATION_EXCEPTION = 700
63
+
55
64
  def __new__(cls) -> ExitCode:
56
65
  """Prevent instantiation."""
57
66
  raise TypeError(f"{cls.__name__} cannot be instantiated.")
@@ -97,25 +106,41 @@ EXIT_CODE_HELP = {
97
106
  "The strategy encountered an error during aggregation. Please check the logs "
98
107
  "for more details."
99
108
  ),
109
+ ExitCode.SERVERAPP_RUN_START_REJECTED: (
110
+ "The SuperLink rejected the request to start the run. This may occur if the "
111
+ "run has been stopped, the run ID or FAB is invalid, or the run failed to "
112
+ "start within the allowed time."
113
+ ),
100
114
  # SuperNode-specific exit codes (300-399)
101
115
  ExitCode.SUPERNODE_REST_ADDRESS_INVALID: (
102
116
  "When using the REST API, please provide `https://` or "
103
117
  "`http://` before the server address (e.g. `http://127.0.0.1:8080`)"
104
118
  ),
105
- ExitCode.SUPERNODE_NODE_AUTH_KEYS_REQUIRED: (
106
- "Node authentication requires file paths to both "
107
- "'--auth-supernode-private-key' and '--auth-supernode-public-key' "
108
- "to be provided (providing only one of them is not sufficient)."
109
- ),
110
- ExitCode.SUPERNODE_NODE_AUTH_KEYS_INVALID: (
111
- "Node authentication requires elliptic curve private and public key pair. "
112
- "Please ensure that the file path points to a valid private/public key "
119
+ ExitCode.SUPERNODE_NODE_AUTH_KEY_INVALID: (
120
+ "Node authentication requires elliptic curve private key. "
121
+ "Please ensure that the file path points to a valid private key "
113
122
  "file and try again."
114
123
  ),
124
+ ExitCode.SUPERNODE_STARTED_WITHOUT_TLS_BUT_NODE_AUTH_ENABLED: (
125
+ "The private key for SuperNode authentication was provided, but TLS is not "
126
+ "enabled. Node authentication can only be used when TLS is enabled."
127
+ ),
128
+ ExitCode.SUPERNODE_INVALID_TRUSTED_ENTITIES: (
129
+ "Failed to read the trusted entities YAML file. "
130
+ "Please ensure that a valid file is provided using "
131
+ "the `--trusted-entities` option."
132
+ ),
115
133
  # SuperExec-specific exit codes (400-499)
116
134
  ExitCode.SUPEREXEC_INVALID_PLUGIN_CONFIG: (
117
135
  "The YAML configuration for the SuperExec plugin is invalid."
118
136
  ),
137
+ # FlowerCLI-specific exit codes (500-599)
138
+ ExitCode.FLWRCLI_NODE_AUTH_PUBLIC_KEY_INVALID: (
139
+ "Node authentication requires a valid elliptic curve public key in the "
140
+ "SSH format and following a NIST standard elliptic curve (e.g. SECP384R1). "
141
+ "Please ensure that the file path points to a valid public key "
142
+ "file and try again."
143
+ ),
119
144
  # Common exit codes (600-699)
120
145
  ExitCode.COMMON_ADDRESS_INVALID: (
121
146
  "Please provide a valid URL, IPv4 or IPv6 address."
@@ -128,4 +153,8 @@ To use the REST API, install `flwr` with the `rest` extra:
128
153
  `pip install "flwr[rest]"`.
129
154
  """,
130
155
  ExitCode.COMMON_TLS_NOT_SUPPORTED: "Please use the '--insecure' flag.",
156
+ # Simulation exit codes (700-799)
157
+ ExitCode.SIMULATION_EXCEPTION: (
158
+ "An unhandled exception occurred when running the simulation."
159
+ ),
131
160
  }
@@ -17,7 +17,7 @@
17
17
 
18
18
  import signal
19
19
  import threading
20
- from typing import Callable
20
+ from collections.abc import Callable
21
21
 
22
22
  from .exit_code import ExitCode
23
23
 
@@ -58,5 +58,9 @@ def trigger_exit_handlers() -> None:
58
58
  """Trigger all registered exit handlers in LIFO order."""
59
59
  with _lock_handlers:
60
60
  for handler in reversed(registered_exit_handlers):
61
- handler()
61
+ try:
62
+ handler()
63
+ except Exception: # pylint: disable=broad-exception-caught
64
+ # Ignore exceptions in exit handlers
65
+ pass
62
66
  registered_exit_handlers.clear()
@@ -16,9 +16,9 @@
16
16
 
17
17
 
18
18
  import signal
19
+ from collections.abc import Callable
19
20
  from threading import Thread
20
21
  from types import FrameType
21
- from typing import Callable, Optional
22
22
 
23
23
  from grpc import Server
24
24
 
@@ -40,10 +40,10 @@ if hasattr(signal, "SIGQUIT"):
40
40
 
41
41
  def register_signal_handlers(
42
42
  event_type: EventType,
43
- exit_message: Optional[str] = None,
44
- grpc_servers: Optional[list[Server]] = None,
45
- bckg_threads: Optional[list[Thread]] = None,
46
- exit_handlers: Optional[list[Callable[[], None]]] = None,
43
+ exit_message: str | None = None,
44
+ grpc_servers: list[Server] | None = None,
45
+ bckg_threads: list[Thread] | None = None,
46
+ exit_handlers: list[Callable[[], None]] | None = None,
47
47
  ) -> None:
48
48
  """Register exit handlers for `SIGINT`, `SIGTERM` and `SIGQUIT` signals.
49
49
 
flwr/common/grpc.py CHANGED
@@ -18,13 +18,14 @@
18
18
  import concurrent.futures
19
19
  import os
20
20
  import sys
21
- from collections.abc import Sequence
21
+ from collections.abc import Callable, Sequence
22
22
  from logging import DEBUG, ERROR
23
- from typing import Any, Callable, Optional
23
+ from typing import Any
24
24
 
25
25
  import grpc
26
26
 
27
- from .address import is_port_in_use
27
+ from flwr.supercore.address import is_port_in_use
28
+
28
29
  from .logger import log
29
30
 
30
31
  GRPC_MAX_MESSAGE_LENGTH: int = 2_147_483_647 # == 2048 * 1024 * 1024 -1 (2GB)
@@ -46,9 +47,9 @@ if "GRPC_VERBOSITY" not in os.environ:
46
47
  def create_channel(
47
48
  server_address: str,
48
49
  insecure: bool,
49
- root_certificates: Optional[bytes] = None,
50
+ root_certificates: bytes | None = None,
50
51
  max_message_length: int = GRPC_MAX_MESSAGE_LENGTH,
51
- interceptors: Optional[Sequence[grpc.UnaryUnaryClientInterceptor]] = None,
52
+ interceptors: Sequence[grpc.UnaryUnaryClientInterceptor] | None = None,
52
53
  ) -> grpc.Channel:
53
54
  """Create a gRPC channel, either secure or insecure."""
54
55
  # Check for conflicting parameters
@@ -104,8 +105,8 @@ def generic_create_grpc_server( # pylint: disable=too-many-arguments, R0914, R0
104
105
  max_concurrent_workers: int = 1000,
105
106
  max_message_length: int = GRPC_MAX_MESSAGE_LENGTH,
106
107
  keepalive_time_ms: int = 210000,
107
- certificates: Optional[tuple[bytes, bytes, bytes]] = None,
108
- interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
108
+ certificates: tuple[bytes, bytes, bytes] | None = None,
109
+ interceptors: Sequence[grpc.ServerInterceptor] | None = None,
109
110
  ) -> grpc.Server:
110
111
  """Create a gRPC server with a single servicer.
111
112
 
@@ -15,7 +15,7 @@
15
15
  """InflatableObject gRPC utils."""
16
16
 
17
17
 
18
- from typing import Callable
18
+ from collections.abc import Callable
19
19
 
20
20
  from flwr.proto.message_pb2 import ( # pylint: disable=E0611
21
21
  ConfirmMessageReceivedRequest,