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.
- flwr/__init__.py +17 -6
- flwr/app/__init__.py +4 -1
- flwr/app/error.py +2 -2
- flwr/app/exception.py +3 -3
- flwr/app/message_type.py +29 -0
- flwr/app/metadata.py +5 -2
- flwr/app/user_config.py +19 -0
- flwr/cli/app.py +62 -9
- flwr/cli/{new/templates/app/code → app_cmd}/__init__.py +9 -1
- flwr/cli/app_cmd/publish.py +285 -0
- flwr/cli/app_cmd/review.py +262 -0
- flwr/cli/auth_plugin/__init__.py +13 -6
- flwr/cli/auth_plugin/auth_plugin.py +26 -15
- flwr/cli/auth_plugin/noop_auth_plugin.py +101 -0
- flwr/cli/auth_plugin/oidc_cli_plugin.py +52 -32
- flwr/cli/build.py +166 -53
- flwr/cli/{cli_user_auth_interceptor.py → cli_account_auth_interceptor.py} +27 -10
- flwr/cli/config/__init__.py +21 -0
- flwr/cli/config/ls.py +104 -0
- flwr/cli/config_migration.py +300 -0
- flwr/cli/config_utils.py +154 -13
- flwr/cli/constant.py +67 -0
- flwr/cli/{new/templates/app/code/flwr_tune → federation}/__init__.py +8 -1
- flwr/cli/federation/ls.py +361 -0
- flwr/cli/flower_config.py +447 -0
- flwr/cli/install.py +91 -13
- flwr/cli/log.py +65 -36
- flwr/cli/login/login.py +41 -27
- flwr/cli/ls.py +232 -158
- flwr/cli/new/new.py +188 -244
- flwr/cli/pull.py +25 -34
- flwr/cli/run/run.py +106 -74
- flwr/cli/run_utils.py +148 -0
- flwr/cli/stop.py +46 -37
- flwr/cli/supernode/__init__.py +25 -0
- flwr/cli/supernode/ls.py +273 -0
- flwr/cli/supernode/register.py +190 -0
- flwr/cli/supernode/unregister.py +140 -0
- flwr/cli/typing.py +211 -0
- flwr/cli/utils.py +428 -80
- flwr/client/__init__.py +2 -1
- flwr/client/dpfedavg_numpy_client.py +4 -1
- flwr/client/grpc_adapter_client/connection.py +14 -17
- flwr/client/grpc_rere_client/connection.py +73 -43
- flwr/client/grpc_rere_client/grpc_adapter.py +35 -15
- flwr/client/grpc_rere_client/{client_interceptor.py → node_auth_client_interceptor.py} +5 -7
- flwr/client/message_handler/message_handler.py +4 -3
- flwr/client/mod/centraldp_mods.py +1 -1
- flwr/client/mod/localdp_mod.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +11 -9
- flwr/client/numpy_client.py +1 -1
- flwr/client/rest_client/connection.py +99 -54
- flwr/client/run_info_store.py +6 -6
- flwr/client/typing.py +1 -1
- flwr/clientapp/__init__.py +1 -2
- flwr/{client → clientapp}/client_app.py +11 -11
- flwr/clientapp/mod/centraldp_mods.py +16 -17
- flwr/clientapp/mod/localdp_mod.py +8 -9
- flwr/clientapp/typing.py +1 -1
- flwr/{client/clientapp → clientapp}/utils.py +4 -4
- flwr/common/__init__.py +3 -2
- flwr/common/args.py +3 -4
- flwr/common/config.py +15 -17
- flwr/common/constant.py +56 -28
- flwr/common/context.py +2 -1
- flwr/common/differential_privacy.py +3 -4
- flwr/common/event_log_plugin/event_log_plugin.py +3 -4
- flwr/common/exit/exit.py +16 -3
- flwr/common/exit/exit_code.py +39 -10
- flwr/common/exit/exit_handler.py +6 -2
- flwr/common/exit/signal_handler.py +5 -5
- flwr/common/grpc.py +8 -7
- flwr/common/inflatable_protobuf_utils.py +1 -1
- flwr/common/inflatable_utils.py +48 -31
- flwr/common/logger.py +19 -19
- flwr/common/message.py +5 -5
- flwr/common/object_ref.py +7 -7
- flwr/common/record/array.py +6 -6
- flwr/common/record/arrayrecord.py +18 -21
- flwr/common/record/configrecord.py +3 -3
- flwr/common/record/recorddict.py +5 -5
- flwr/common/record/typeddict.py +9 -2
- flwr/common/recorddict_compat.py +7 -10
- flwr/common/retry_invoker.py +20 -20
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -89
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +8 -5
- flwr/common/serde.py +22 -11
- flwr/common/serde_utils.py +2 -2
- flwr/common/telemetry.py +10 -6
- flwr/common/typing.py +65 -44
- flwr/compat/client/app.py +45 -47
- flwr/compat/client/grpc_client/connection.py +15 -14
- flwr/compat/common/constant.py +29 -0
- flwr/compat/server/app.py +6 -7
- flwr/proto/appio_pb2.py +13 -3
- flwr/proto/appio_pb2.pyi +134 -65
- flwr/proto/appio_pb2_grpc.py +20 -0
- flwr/proto/appio_pb2_grpc.pyi +27 -0
- flwr/proto/clientappio_pb2.py +17 -7
- flwr/proto/clientappio_pb2.pyi +15 -0
- flwr/proto/clientappio_pb2_grpc.py +206 -40
- flwr/proto/clientappio_pb2_grpc.pyi +168 -53
- flwr/proto/control_pb2.py +72 -40
- flwr/proto/control_pb2.pyi +319 -87
- flwr/proto/control_pb2_grpc.py +339 -28
- flwr/proto/control_pb2_grpc.pyi +209 -37
- flwr/proto/error_pb2.py +13 -3
- flwr/proto/error_pb2.pyi +24 -6
- flwr/proto/error_pb2_grpc.py +20 -0
- flwr/proto/error_pb2_grpc.pyi +27 -0
- flwr/proto/fab_pb2.py +24 -10
- flwr/proto/fab_pb2.pyi +68 -20
- flwr/proto/fab_pb2_grpc.py +20 -0
- flwr/proto/fab_pb2_grpc.pyi +27 -0
- flwr/proto/federation_pb2.py +38 -0
- flwr/proto/federation_pb2.pyi +56 -0
- flwr/proto/federation_pb2_grpc.py +24 -0
- flwr/proto/federation_pb2_grpc.pyi +31 -0
- flwr/proto/fleet_pb2.py +45 -27
- flwr/proto/fleet_pb2.pyi +190 -70
- flwr/proto/fleet_pb2_grpc.py +277 -66
- flwr/proto/fleet_pb2_grpc.pyi +201 -55
- flwr/proto/grpcadapter_pb2.py +14 -4
- flwr/proto/grpcadapter_pb2.pyi +38 -16
- flwr/proto/grpcadapter_pb2_grpc.py +35 -4
- flwr/proto/grpcadapter_pb2_grpc.pyi +38 -7
- flwr/proto/heartbeat_pb2.py +17 -7
- flwr/proto/heartbeat_pb2.pyi +51 -22
- flwr/proto/heartbeat_pb2_grpc.py +20 -0
- flwr/proto/heartbeat_pb2_grpc.pyi +27 -0
- flwr/proto/log_pb2.py +13 -3
- flwr/proto/log_pb2.pyi +34 -11
- flwr/proto/log_pb2_grpc.py +20 -0
- flwr/proto/log_pb2_grpc.pyi +27 -0
- flwr/proto/message_pb2.py +15 -5
- flwr/proto/message_pb2.pyi +154 -86
- flwr/proto/message_pb2_grpc.py +20 -0
- flwr/proto/message_pb2_grpc.pyi +27 -0
- flwr/proto/node_pb2.py +16 -4
- flwr/proto/node_pb2.pyi +77 -4
- flwr/proto/node_pb2_grpc.py +20 -0
- flwr/proto/node_pb2_grpc.pyi +27 -0
- flwr/proto/recorddict_pb2.py +13 -3
- flwr/proto/recorddict_pb2.pyi +184 -107
- flwr/proto/recorddict_pb2_grpc.py +20 -0
- flwr/proto/recorddict_pb2_grpc.pyi +27 -0
- flwr/proto/run_pb2.py +40 -31
- flwr/proto/run_pb2.pyi +158 -84
- flwr/proto/run_pb2_grpc.py +20 -0
- flwr/proto/run_pb2_grpc.pyi +27 -0
- flwr/proto/serverappio_pb2.py +13 -3
- flwr/proto/serverappio_pb2.pyi +32 -8
- flwr/proto/serverappio_pb2_grpc.py +246 -65
- flwr/proto/serverappio_pb2_grpc.pyi +221 -85
- flwr/proto/simulationio_pb2.py +16 -8
- flwr/proto/simulationio_pb2.pyi +15 -0
- flwr/proto/simulationio_pb2_grpc.py +162 -41
- flwr/proto/simulationio_pb2_grpc.pyi +149 -55
- flwr/proto/transport_pb2.py +20 -10
- flwr/proto/transport_pb2.pyi +249 -160
- flwr/proto/transport_pb2_grpc.py +35 -4
- flwr/proto/transport_pb2_grpc.pyi +38 -8
- flwr/server/app.py +175 -128
- flwr/server/client_manager.py +4 -5
- flwr/server/client_proxy.py +10 -11
- flwr/server/compat/app.py +4 -5
- flwr/server/compat/app_utils.py +2 -1
- flwr/server/compat/grid_client_proxy.py +12 -13
- flwr/server/compat/legacy_context.py +3 -4
- flwr/server/fleet_event_log_interceptor.py +2 -1
- flwr/server/grid/grid.py +2 -3
- flwr/server/grid/grpc_grid.py +12 -10
- flwr/server/grid/inmemory_grid.py +4 -4
- flwr/server/run_serverapp.py +2 -3
- flwr/server/server.py +34 -39
- flwr/server/server_app.py +7 -8
- flwr/server/server_config.py +1 -2
- flwr/server/serverapp/app.py +34 -28
- flwr/server/serverapp_components.py +4 -5
- flwr/server/strategy/aggregate.py +9 -8
- flwr/server/strategy/bulyan.py +13 -11
- flwr/server/strategy/dp_adaptive_clipping.py +16 -20
- flwr/server/strategy/dp_fixed_clipping.py +12 -17
- flwr/server/strategy/dpfedavg_adaptive.py +3 -4
- flwr/server/strategy/dpfedavg_fixed.py +6 -10
- flwr/server/strategy/fault_tolerant_fedavg.py +14 -13
- flwr/server/strategy/fedadagrad.py +18 -14
- flwr/server/strategy/fedadam.py +16 -14
- flwr/server/strategy/fedavg.py +16 -17
- flwr/server/strategy/fedavg_android.py +15 -15
- flwr/server/strategy/fedavgm.py +21 -18
- flwr/server/strategy/fedmedian.py +2 -3
- flwr/server/strategy/fedopt.py +11 -10
- flwr/server/strategy/fedprox.py +10 -9
- flwr/server/strategy/fedtrimmedavg.py +12 -11
- flwr/server/strategy/fedxgb_bagging.py +13 -11
- flwr/server/strategy/fedxgb_cyclic.py +6 -6
- flwr/server/strategy/fedxgb_nn_avg.py +4 -4
- flwr/server/strategy/fedyogi.py +16 -14
- flwr/server/strategy/krum.py +12 -11
- flwr/server/strategy/qfedavg.py +16 -15
- flwr/server/strategy/strategy.py +6 -9
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +20 -9
- flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -2
- flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -4
- flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +10 -12
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +1 -3
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +136 -42
- flwr/server/superlink/fleet/grpc_rere/{server_interceptor.py → node_auth_server_interceptor.py} +28 -50
- flwr/server/superlink/fleet/message_handler/message_handler.py +141 -51
- flwr/server/superlink/fleet/rest_rere/rest_api.py +54 -33
- flwr/server/superlink/fleet/vce/backend/backend.py +2 -2
- flwr/server/superlink/fleet/vce/backend/raybackend.py +6 -6
- flwr/server/superlink/fleet/vce/vce_api.py +32 -13
- flwr/server/superlink/linkstate/__init__.py +2 -0
- flwr/server/superlink/linkstate/in_memory_linkstate.py +293 -208
- flwr/server/superlink/linkstate/linkstate.py +176 -64
- flwr/server/superlink/linkstate/linkstate_factory.py +24 -6
- flwr/server/superlink/linkstate/sql_linkstate.py +221 -0
- flwr/server/superlink/linkstate/sqlite_linkstate.py +743 -648
- flwr/server/superlink/linkstate/utils.py +11 -62
- flwr/server/superlink/serverappio/serverappio_grpc.py +1 -2
- flwr/server/superlink/serverappio/serverappio_servicer.py +28 -23
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -2
- flwr/server/superlink/simulation/simulationio_servicer.py +19 -14
- flwr/server/superlink/utils.py +4 -6
- flwr/server/typing.py +1 -1
- flwr/server/utils/tensorboard.py +15 -8
- flwr/server/utils/validator.py +2 -3
- flwr/server/workflow/default_workflows.py +7 -6
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +2 -4
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +13 -11
- flwr/serverapp/strategy/bulyan.py +16 -15
- flwr/serverapp/strategy/dp_adaptive_clipping.py +12 -11
- flwr/serverapp/strategy/dp_fixed_clipping.py +11 -14
- flwr/serverapp/strategy/fedadagrad.py +10 -11
- flwr/serverapp/strategy/fedadam.py +10 -11
- flwr/serverapp/strategy/fedavg.py +10 -11
- flwr/serverapp/strategy/fedavgm.py +17 -16
- flwr/serverapp/strategy/fedmedian.py +2 -2
- flwr/serverapp/strategy/fedopt.py +10 -11
- flwr/serverapp/strategy/fedprox.py +7 -8
- flwr/serverapp/strategy/fedtrimmedavg.py +9 -9
- flwr/serverapp/strategy/fedxgb_bagging.py +3 -3
- flwr/serverapp/strategy/fedxgb_cyclic.py +10 -10
- flwr/serverapp/strategy/fedyogi.py +9 -11
- flwr/serverapp/strategy/krum.py +7 -7
- flwr/serverapp/strategy/multikrum.py +9 -9
- flwr/serverapp/strategy/qfedavg.py +17 -16
- flwr/serverapp/strategy/strategy.py +6 -9
- flwr/serverapp/strategy/strategy_utils.py +7 -8
- flwr/simulation/app.py +46 -42
- flwr/simulation/legacy_app.py +12 -12
- flwr/simulation/ray_transport/ray_actor.py +11 -12
- flwr/simulation/ray_transport/ray_client_proxy.py +14 -19
- flwr/simulation/run_simulation.py +46 -44
- flwr/simulation/simulationio_connection.py +4 -4
- flwr/{common → supercore}/address.py +1 -37
- flwr/supercore/cli/flower_superexec.py +3 -4
- flwr/supercore/constant.py +69 -0
- flwr/supercore/corestate/corestate.py +24 -3
- flwr/supercore/corestate/in_memory_corestate.py +138 -0
- flwr/supercore/corestate/sql_corestate.py +153 -0
- flwr/supercore/corestate/sqlite_corestate.py +157 -0
- flwr/supercore/credential_store/__init__.py +33 -0
- flwr/supercore/credential_store/credential_store.py +34 -0
- flwr/supercore/credential_store/file_credential_store.py +76 -0
- flwr/{common → supercore}/date.py +0 -11
- flwr/supercore/ffs/disk_ffs.py +1 -2
- flwr/supercore/ffs/ffs.py +1 -2
- flwr/supercore/ffs/ffs_factory.py +1 -2
- flwr/{common → supercore}/heartbeat.py +20 -25
- flwr/supercore/object_store/in_memory_object_store.py +1 -6
- flwr/supercore/object_store/object_store.py +1 -2
- flwr/supercore/object_store/object_store_factory.py +27 -8
- flwr/supercore/object_store/sqlite_object_store.py +253 -0
- flwr/{cli/new/templates/app → supercore/primitives}/__init__.py +1 -1
- flwr/supercore/primitives/asymmetric.py +117 -0
- flwr/supercore/primitives/asymmetric_ed25519.py +175 -0
- flwr/supercore/sql_mixin.py +292 -0
- flwr/supercore/sqlite_mixin.py +156 -0
- flwr/{client/clientapp → supercore/state}/__init__.py +2 -2
- flwr/supercore/state/schema/README.md +125 -0
- flwr/{cli/new/templates → supercore/state/schema}/__init__.py +2 -2
- flwr/supercore/state/schema/corestate_tables.py +36 -0
- flwr/supercore/state/schema/linkstate_tables.py +152 -0
- flwr/supercore/state/schema/objectstore_tables.py +90 -0
- flwr/supercore/superexec/plugin/base_exec_plugin.py +1 -2
- flwr/supercore/superexec/plugin/exec_plugin.py +3 -3
- flwr/supercore/superexec/run_superexec.py +9 -13
- flwr/supercore/utils.py +224 -0
- flwr/superlink/artifact_provider/artifact_provider.py +1 -2
- flwr/superlink/auth_plugin/__init__.py +5 -2
- flwr/superlink/auth_plugin/auth_plugin.py +20 -19
- flwr/superlink/auth_plugin/noop_auth_plugin.py +84 -0
- flwr/superlink/federation/__init__.py +24 -0
- flwr/superlink/federation/federation_manager.py +64 -0
- flwr/superlink/federation/noop_federation_manager.py +71 -0
- flwr/superlink/servicer/control/{control_user_auth_interceptor.py → control_account_auth_interceptor.py} +41 -32
- flwr/superlink/servicer/control/control_event_log_interceptor.py +7 -7
- flwr/superlink/servicer/control/control_grpc.py +20 -17
- flwr/superlink/servicer/control/control_license_interceptor.py +3 -3
- flwr/superlink/servicer/control/control_servicer.py +328 -68
- flwr/supernode/cli/flower_supernode.py +74 -26
- flwr/supernode/nodestate/in_memory_nodestate.py +121 -49
- flwr/supernode/nodestate/nodestate.py +52 -8
- flwr/supernode/nodestate/nodestate_factory.py +7 -4
- flwr/supernode/runtime/run_clientapp.py +43 -24
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +48 -10
- flwr/supernode/start_client_internal.py +185 -57
- {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/METADATA +10 -11
- flwr_nightly-1.26.0.dev20260121.dist-info/RECORD +411 -0
- flwr/cli/new/templates/app/.gitignore.tpl +0 -163
- flwr/cli/new/templates/app/LICENSE.tpl +0 -202
- flwr/cli/new/templates/app/README.baseline.md.tpl +0 -127
- flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -68
- flwr/cli/new/templates/app/README.md.tpl +0 -37
- flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/code/__init__.py.tpl +0 -1
- flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +0 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +0 -75
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -93
- flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -71
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -102
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -46
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -80
- flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +0 -55
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -108
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -82
- flwr/cli/new/templates/app/code/client.xgboost.py.tpl +0 -110
- flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -36
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -92
- flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -87
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -56
- flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -73
- flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -78
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -66
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +0 -43
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -42
- flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -39
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -41
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -38
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -41
- flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +0 -31
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -44
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -38
- flwr/cli/new/templates/app/code/server.xgboost.py.tpl +0 -56
- flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -98
- flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -57
- flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -102
- flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -7
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -98
- flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +0 -111
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -67
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -52
- flwr/cli/new/templates/app/code/task.xgboost.py.tpl +0 -67
- flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +0 -146
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +0 -80
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +0 -65
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +0 -52
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +0 -56
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -49
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +0 -52
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +0 -61
- flwr/common/pyproject.py +0 -42
- flwr/supercore/object_store/utils.py +0 -43
- flwr_nightly-1.23.0.dev20250930.dist-info/RECORD +0 -429
- /flwr/{common → supercore}/version.py +0 -0
- {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/WHEEL +0 -0
- {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,
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
125
|
+
# Constants for account authentication
|
|
113
126
|
CREDENTIALS_DIR = ".credentials"
|
|
114
|
-
|
|
115
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
148
|
-
|
|
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
|
-
|
|
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
|
-
|
|
163
|
-
|
|
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
|
|
249
|
-
"""
|
|
260
|
+
class AuthnType:
|
|
261
|
+
"""Account authentication types."""
|
|
250
262
|
|
|
263
|
+
NOOP = "noop"
|
|
251
264
|
OIDC = "oidc"
|
|
252
265
|
|
|
253
|
-
def __new__(cls) ->
|
|
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
|
@@ -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:
|
|
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:
|
|
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:
|
|
48
|
+
account_info: AccountInfo | None,
|
|
50
49
|
method_name: str,
|
|
51
|
-
response:
|
|
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
|
-
|
|
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.
|
|
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
|
|
flwr/common/exit/exit_code.py
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
106
|
-
"Node authentication requires
|
|
107
|
-
"
|
|
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
|
}
|
flwr/common/exit/exit_handler.py
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import signal
|
|
19
19
|
import threading
|
|
20
|
-
from
|
|
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
|
-
|
|
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:
|
|
44
|
-
grpc_servers:
|
|
45
|
-
bckg_threads:
|
|
46
|
-
exit_handlers:
|
|
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
|
|
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:
|
|
50
|
+
root_certificates: bytes | None = None,
|
|
50
51
|
max_message_length: int = GRPC_MAX_MESSAGE_LENGTH,
|
|
51
|
-
interceptors:
|
|
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:
|
|
108
|
-
interceptors:
|
|
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
|
|