flwr 1.14.0__tar.gz → 1.15.0__tar.gz
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-1.14.0 → flwr-1.15.0}/PKG-INFO +8 -8
- {flwr-1.14.0 → flwr-1.15.0}/README.md +2 -2
- {flwr-1.14.0 → flwr-1.15.0}/pyproject.toml +18 -16
- flwr-1.15.0/src/py/flwr/cli/auth_plugin/__init__.py +31 -0
- flwr-1.15.0/src/py/flwr/cli/auth_plugin/oidc_cli_plugin.py +150 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/cli_user_auth_interceptor.py +6 -2
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/config_utils.py +24 -147
- flwr-1.15.0/src/py/flwr/cli/constant.py +27 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/install.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/log.py +18 -3
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/login/login.py +43 -8
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/ls.py +14 -5
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/README.md.tpl +3 -2
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +2 -2
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/pyproject.jax.toml.tpl +2 -2
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +2 -2
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +2 -2
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +4 -4
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +3 -3
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +2 -2
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/run/run.py +21 -11
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/stop.py +13 -4
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/utils.py +54 -40
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/app.py +36 -48
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/clientapp/app.py +19 -25
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/clientapp/utils.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/grpc_client/connection.py +1 -12
- flwr-1.15.0/src/py/flwr/client/grpc_rere_client/client_interceptor.py +70 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/grpc_rere_client/connection.py +46 -36
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/grpc_rere_client/grpc_adapter.py +12 -12
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/message_handler/task_handler.py +0 -17
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/rest_client/connection.py +34 -26
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/supernode/app.py +18 -72
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/args.py +25 -47
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/auth_plugin/auth_plugin.py +34 -23
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/config.py +166 -16
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/constant.py +22 -9
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/differential_privacy.py +2 -1
- flwr-1.15.0/src/py/flwr/common/exit/__init__.py +24 -0
- flwr-1.15.0/src/py/flwr/common/exit/exit.py +99 -0
- flwr-1.15.0/src/py/flwr/common/exit/exit_code.py +93 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/exit_handlers.py +24 -10
- flwr-1.14.0/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_server.py → flwr-1.15.0/src/py/flwr/common/grpc.py +62 -120
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/logger.py +26 -7
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/record/recordset.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/secure_aggregation/crypto/symmetric_encryption.py +45 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/serde.py +6 -4
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/typing.py +20 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/clientappio_pb2.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/error_pb2.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/exec_pb2.py +13 -25
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/exec_pb2.pyi +27 -54
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/fab_pb2.py +1 -1
- flwr-1.15.0/src/py/flwr/proto/fleet_pb2.py +56 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/fleet_pb2.pyi +23 -23
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/fleet_pb2_grpc.py +30 -30
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/fleet_pb2_grpc.pyi +20 -20
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/grpcadapter_pb2.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/log_pb2.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/message_pb2.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/node_pb2.py +3 -3
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/node_pb2.pyi +1 -4
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/recordset_pb2.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/run_pb2.py +1 -1
- flwr-1.15.0/src/py/flwr/proto/serverappio_pb2.py +51 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/serverappio_pb2.pyi +26 -32
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/serverappio_pb2_grpc.py +28 -28
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/serverappio_pb2_grpc.pyi +16 -16
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/simulationio_pb2.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/task_pb2.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/transport_pb2.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/app.py +116 -128
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/compat/app_utils.py +0 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/compat/driver_client_proxy.py +1 -2
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/driver/grpc_driver.py +32 -27
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/driver/inmemory_driver.py +2 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/serverapp/app.py +12 -10
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/driver/serverappio_grpc.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/driver/serverappio_servicer.py +74 -48
- flwr-1.15.0/src/py/flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +94 -0
- flwr-1.15.0/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +126 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +25 -24
- flwr-1.15.0/src/py/flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +156 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/message_handler/message_handler.py +37 -24
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/rest_rere/rest_api.py +16 -18
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/vce/vce_api.py +2 -2
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/linkstate/in_memory_linkstate.py +45 -75
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/linkstate/linkstate.py +17 -38
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/linkstate/sqlite_linkstate.py +81 -145
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/linkstate/utils.py +18 -8
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/utils/validator.py +9 -34
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/simulation/app.py +4 -6
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/simulation/legacy_app.py +4 -2
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/simulation/run_simulation.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/simulation/simulationio_connection.py +2 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/superexec/exec_grpc.py +1 -1
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/superexec/exec_servicer.py +23 -2
- flwr-1.14.0/src/py/flwr/client/grpc_rere_client/client_interceptor.py +0 -170
- flwr-1.14.0/src/py/flwr/common/grpc.py +0 -68
- flwr-1.14.0/src/py/flwr/proto/fleet_pb2.py +0 -56
- flwr-1.14.0/src/py/flwr/proto/serverappio_pb2.py +0 -52
- flwr-1.14.0/src/py/flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +0 -162
- flwr-1.14.0/src/py/flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +0 -227
- {flwr-1.14.0 → flwr-1.15.0}/LICENSE +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/app.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/build.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/example.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/login/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/new.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/.gitignore.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/LICENSE.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/README.baseline.md.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/__init__.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/client.baseline.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/flwr_tune/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/server.baseline.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/cli/run/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/client.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/client_app.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/clientapp/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/clientapp/clientappio_servicer.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/dpfedavg_numpy_client.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/grpc_adapter_client/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/grpc_adapter_client/connection.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/grpc_client/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/grpc_rere_client/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/heartbeat.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/message_handler/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/message_handler/message_handler.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/mod/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/mod/centraldp_mods.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/mod/comms_mods.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/mod/localdp_mod.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/mod/secure_aggregation/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/mod/secure_aggregation/secagg_mod.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/mod/secure_aggregation/secaggplus_mod.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/mod/utils.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/nodestate/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/nodestate/in_memory_nodestate.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/nodestate/nodestate.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/nodestate/nodestate_factory.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/numpy_client.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/rest_client/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/run_info_store.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/supernode/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/client/typing.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/address.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/auth_plugin/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/context.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/date.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/differential_privacy_constants.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/dp.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/message.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/object_ref.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/parameter.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/pyproject.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/record/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/record/configsrecord.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/record/conversion_utils.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/record/metricsrecord.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/record/parametersrecord.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/record/typeddict.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/recordset_compat.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/retry_invoker.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/secure_aggregation/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/secure_aggregation/crypto/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/secure_aggregation/crypto/shamir.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/secure_aggregation/ndarrays_arithmetic.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/secure_aggregation/quantization.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/secure_aggregation/secaggplus_constants.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/secure_aggregation/secaggplus_utils.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/telemetry.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/common/version.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/clientappio_pb2.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/clientappio_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/clientappio_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/error_pb2.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/error_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/error_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/exec_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/exec_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/fab_pb2.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/fab_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/fab_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/grpcadapter_pb2.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/grpcadapter_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/grpcadapter_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/log_pb2.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/log_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/log_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/message_pb2.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/message_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/message_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/node_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/node_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/recordset_pb2.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/recordset_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/recordset_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/run_pb2.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/run_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/run_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/simulationio_pb2.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/simulationio_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/simulationio_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/task_pb2.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/task_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/task_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/transport_pb2.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/transport_pb2_grpc.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/proto/transport_pb2_grpc.pyi +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/py.typed +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/client_manager.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/client_proxy.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/compat/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/compat/app.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/compat/legacy_context.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/criterion.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/driver/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/driver/driver.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/history.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/run_serverapp.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/server.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/server_app.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/server_config.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/serverapp/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/serverapp_components.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/aggregate.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/bulyan.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/dp_adaptive_clipping.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/dp_fixed_clipping.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/dpfedavg_adaptive.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/dpfedavg_fixed.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fault_tolerant_fedavg.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedadagrad.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedadam.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedavg.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedavg_android.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedavgm.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedmedian.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedopt.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedprox.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedtrimmedavg.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedxgb_bagging.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedxgb_cyclic.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedxgb_nn_avg.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/fedyogi.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/krum.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/qfedavg.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/strategy/strategy.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/driver/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/ffs/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/ffs/disk_ffs.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/ffs/ffs.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/ffs/ffs_factory.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/grpc_adapter/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/grpc_bidi/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/grpc_rere/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/message_handler/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/rest_rere/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/vce/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/vce/backend/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/vce/backend/backend.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/fleet/vce/backend/raybackend.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/linkstate/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/linkstate/linkstate_factory.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/simulation/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/simulation/simulationio_servicer.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/superlink/utils.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/typing.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/utils/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/utils/tensorboard.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/workflow/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/workflow/constant.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/workflow/default_workflows.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/workflow/secure_aggregation/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/workflow/secure_aggregation/secagg_workflow.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/simulation/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/simulation/ray_transport/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/simulation/ray_transport/ray_actor.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/simulation/ray_transport/ray_client_proxy.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/simulation/ray_transport/utils.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/superexec/__init__.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/superexec/app.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/superexec/deployment.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/superexec/exec_user_auth_interceptor.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/superexec/executor.py +0 -0
- {flwr-1.14.0 → flwr-1.15.0}/src/py/flwr/superexec/simulation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flwr
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.15.0
|
|
4
4
|
Summary: Flower: A Friendly Federated AI Framework
|
|
5
5
|
Home-page: https://flower.ai
|
|
6
6
|
License: Apache-2.0
|
|
@@ -32,22 +32,22 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
32
32
|
Classifier: Typing :: Typed
|
|
33
33
|
Provides-Extra: rest
|
|
34
34
|
Provides-Extra: simulation
|
|
35
|
-
Requires-Dist: cryptography (>=
|
|
36
|
-
Requires-Dist: grpcio (>=1.
|
|
35
|
+
Requires-Dist: cryptography (>=43.0.1,<44.0.0)
|
|
36
|
+
Requires-Dist: grpcio (>=1.62.3,<2.0.0,!=1.65.0)
|
|
37
37
|
Requires-Dist: iterators (>=0.0.2,<0.0.3)
|
|
38
38
|
Requires-Dist: numpy (>=1.26.0,<3.0.0)
|
|
39
39
|
Requires-Dist: pathspec (>=0.12.1,<0.13.0)
|
|
40
|
-
Requires-Dist: protobuf (>=4.
|
|
40
|
+
Requires-Dist: protobuf (>=4.21.6,<5.0.0)
|
|
41
41
|
Requires-Dist: pycryptodome (>=3.18.0,<4.0.0)
|
|
42
42
|
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
43
43
|
Requires-Dist: ray (==2.10.0) ; (python_version >= "3.9" and python_version < "3.12") and (extra == "simulation")
|
|
44
44
|
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
45
45
|
Requires-Dist: rich (>=13.5.0,<14.0.0)
|
|
46
|
-
Requires-Dist: starlette (>=0.
|
|
46
|
+
Requires-Dist: starlette (>=0.45.2,<0.46.0) ; extra == "rest"
|
|
47
47
|
Requires-Dist: tomli (>=2.0.1,<3.0.0)
|
|
48
48
|
Requires-Dist: tomli-w (>=1.0.0,<2.0.0)
|
|
49
49
|
Requires-Dist: typer (>=0.12.5,<0.13.0)
|
|
50
|
-
Requires-Dist: uvicorn[standard] (>=0.
|
|
50
|
+
Requires-Dist: uvicorn[standard] (>=0.34.0,<0.35.0) ; extra == "rest"
|
|
51
51
|
Project-URL: Documentation, https://flower.ai
|
|
52
52
|
Project-URL: Repository, https://github.com/adap/flower
|
|
53
53
|
Description-Content-Type: text/markdown
|
|
@@ -88,7 +88,7 @@ design of Flower is based on a few guiding principles:
|
|
|
88
88
|
|
|
89
89
|
- **Framework-agnostic**: Different machine learning frameworks have different
|
|
90
90
|
strengths. Flower can be used with any machine learning framework, for
|
|
91
|
-
example, [PyTorch](https://pytorch.org), [TensorFlow](https://tensorflow.org), [Hugging Face Transformers](https://huggingface.co/), [PyTorch Lightning](https://pytorchlightning.ai/), [scikit-learn](https://scikit-learn.org/), [JAX](https://jax.readthedocs.io/), [TFLite](https://tensorflow.org/lite/), [MONAI](https://docs.monai.io/en/latest/index.html), [fastai](https://www.fast.ai/), [MLX](https://ml-explore.github.io/mlx/build/html/index.html), [XGBoost](https://xgboost.readthedocs.io/en/stable/), [Pandas](https://pandas.pydata.org/) for federated analytics, or even raw [NumPy](https://numpy.org/)
|
|
91
|
+
example, [PyTorch](https://pytorch.org), [TensorFlow](https://tensorflow.org), [Hugging Face Transformers](https://huggingface.co/), [PyTorch Lightning](https://pytorchlightning.ai/), [scikit-learn](https://scikit-learn.org/), [JAX](https://jax.readthedocs.io/), [TFLite](https://tensorflow.org/lite/), [MONAI](https://docs.monai.io/en/latest/index.html), [fastai](https://www.fast.ai/), [MLX](https://ml-explore.github.io/mlx/build/html/index.html), [XGBoost](https://xgboost.readthedocs.io/en/stable/), [LeRobot](https://github.com/huggingface/lerobot) for federated robots, [Pandas](https://pandas.pydata.org/) for federated analytics, or even raw [NumPy](https://numpy.org/)
|
|
92
92
|
for users who enjoy computing gradients by hand.
|
|
93
93
|
|
|
94
94
|
- **Understandable**: Flower is written with maintainability in mind. The
|
|
@@ -118,7 +118,7 @@ Flower's goal is to make federated learning accessible to everyone. This series
|
|
|
118
118
|
|
|
119
119
|
4. **Custom Clients for Federated Learning**
|
|
120
120
|
|
|
121
|
-
[](https://colab.research.google.com/github/adap/flower/blob/main/
|
|
121
|
+
[](https://colab.research.google.com/github/adap/flower/blob/main/framework/docs/source/tutorial-series-customize-the-client-pytorch.ipynb) (or open the [Jupyter Notebook](https://github.com/adap/flower/blob/main/framework/docs/source/tutorial-series-customize-the-client-pytorch.ipynb))
|
|
122
122
|
|
|
123
123
|
Stay tuned, more tutorials are coming soon. Topics include **Privacy and Security in Federated Learning**, and **Scaling Federated Learning**.
|
|
124
124
|
|
|
@@ -34,7 +34,7 @@ design of Flower is based on a few guiding principles:
|
|
|
34
34
|
|
|
35
35
|
- **Framework-agnostic**: Different machine learning frameworks have different
|
|
36
36
|
strengths. Flower can be used with any machine learning framework, for
|
|
37
|
-
example, [PyTorch](https://pytorch.org), [TensorFlow](https://tensorflow.org), [Hugging Face Transformers](https://huggingface.co/), [PyTorch Lightning](https://pytorchlightning.ai/), [scikit-learn](https://scikit-learn.org/), [JAX](https://jax.readthedocs.io/), [TFLite](https://tensorflow.org/lite/), [MONAI](https://docs.monai.io/en/latest/index.html), [fastai](https://www.fast.ai/), [MLX](https://ml-explore.github.io/mlx/build/html/index.html), [XGBoost](https://xgboost.readthedocs.io/en/stable/), [Pandas](https://pandas.pydata.org/) for federated analytics, or even raw [NumPy](https://numpy.org/)
|
|
37
|
+
example, [PyTorch](https://pytorch.org), [TensorFlow](https://tensorflow.org), [Hugging Face Transformers](https://huggingface.co/), [PyTorch Lightning](https://pytorchlightning.ai/), [scikit-learn](https://scikit-learn.org/), [JAX](https://jax.readthedocs.io/), [TFLite](https://tensorflow.org/lite/), [MONAI](https://docs.monai.io/en/latest/index.html), [fastai](https://www.fast.ai/), [MLX](https://ml-explore.github.io/mlx/build/html/index.html), [XGBoost](https://xgboost.readthedocs.io/en/stable/), [LeRobot](https://github.com/huggingface/lerobot) for federated robots, [Pandas](https://pandas.pydata.org/) for federated analytics, or even raw [NumPy](https://numpy.org/)
|
|
38
38
|
for users who enjoy computing gradients by hand.
|
|
39
39
|
|
|
40
40
|
- **Understandable**: Flower is written with maintainability in mind. The
|
|
@@ -64,7 +64,7 @@ Flower's goal is to make federated learning accessible to everyone. This series
|
|
|
64
64
|
|
|
65
65
|
4. **Custom Clients for Federated Learning**
|
|
66
66
|
|
|
67
|
-
[](https://colab.research.google.com/github/adap/flower/blob/main/
|
|
67
|
+
[](https://colab.research.google.com/github/adap/flower/blob/main/framework/docs/source/tutorial-series-customize-the-client-pytorch.ipynb) (or open the [Jupyter Notebook](https://github.com/adap/flower/blob/main/framework/docs/source/tutorial-series-customize-the-client-pytorch.ipynb))
|
|
68
68
|
|
|
69
69
|
Stay tuned, more tutorials are coming soon. Topics include **Privacy and Security in Federated Learning**, and **Scaling Federated Learning**.
|
|
70
70
|
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "flwr"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.15.0"
|
|
8
8
|
description = "Flower: A Friendly Federated AI Framework"
|
|
9
9
|
license = "Apache-2.0"
|
|
10
10
|
authors = ["The Flower Authors <hello@flower.ai>"]
|
|
@@ -68,9 +68,9 @@ flower-client-app = "flwr.client.supernode:run_client_app" # Deprecated
|
|
|
68
68
|
python = "^3.9"
|
|
69
69
|
# Mandatory dependencies
|
|
70
70
|
numpy = ">=1.26.0,<3.0.0"
|
|
71
|
-
grpcio = "^1.
|
|
72
|
-
protobuf = "^4.
|
|
73
|
-
cryptography = "^
|
|
71
|
+
grpcio = "^1.62.3,!=1.65.0"
|
|
72
|
+
protobuf = "^4.21.6"
|
|
73
|
+
cryptography = "^43.0.1"
|
|
74
74
|
pycryptodome = "^3.18.0"
|
|
75
75
|
iterators = "^0.0.2"
|
|
76
76
|
typer = "^0.12.5"
|
|
@@ -83,8 +83,8 @@ requests = "^2.31.0"
|
|
|
83
83
|
# Optional dependencies (Simulation Engine)
|
|
84
84
|
ray = { version = "==2.10.0", optional = true, python = ">=3.9,<3.12" }
|
|
85
85
|
# Optional dependencies (REST transport layer)
|
|
86
|
-
starlette = { version = "^0.
|
|
87
|
-
uvicorn = { version = "^0.
|
|
86
|
+
starlette = { version = "^0.45.2", optional = true }
|
|
87
|
+
uvicorn = { version = "^0.34.0", extras = ["standard"], optional = true }
|
|
88
88
|
|
|
89
89
|
[tool.poetry.extras]
|
|
90
90
|
simulation = ["ray"]
|
|
@@ -92,7 +92,7 @@ rest = ["starlette", "uvicorn"]
|
|
|
92
92
|
|
|
93
93
|
[tool.poetry.group.dev.dependencies]
|
|
94
94
|
types-dataclasses = "==0.6.6"
|
|
95
|
-
types-protobuf = "==
|
|
95
|
+
types-protobuf = "==4.21.0.7"
|
|
96
96
|
types-requests = "==2.31.0.20240125"
|
|
97
97
|
types-setuptools = "==69.0.0.20240125"
|
|
98
98
|
clang-format = "==17.0.6"
|
|
@@ -106,11 +106,11 @@ flake8 = "==5.0.4"
|
|
|
106
106
|
parameterized = "==0.9.0"
|
|
107
107
|
pytest = "==7.4.4"
|
|
108
108
|
pytest-cov = "==4.1.0"
|
|
109
|
-
pytest-watcher = "==0.4.
|
|
110
|
-
grpcio-tools = "==1.
|
|
109
|
+
pytest-watcher = "==0.4.3"
|
|
110
|
+
grpcio-tools = "==1.62.3"
|
|
111
111
|
mypy-protobuf = "==3.2.0"
|
|
112
112
|
jupyterlab = "==4.0.12"
|
|
113
|
-
rope = "==1.
|
|
113
|
+
rope = "==1.13.0"
|
|
114
114
|
semver = "==3.0.2"
|
|
115
115
|
sphinx = "==7.4.7"
|
|
116
116
|
sphinx-intl = "==2.2.0"
|
|
@@ -124,7 +124,7 @@ furo = "==2024.8.6"
|
|
|
124
124
|
sphinx-reredirects = "==0.1.5"
|
|
125
125
|
nbsphinx = "==0.9.5"
|
|
126
126
|
nbstripout = "==0.6.1"
|
|
127
|
-
ruff = "==0.
|
|
127
|
+
ruff = "==0.4.5"
|
|
128
128
|
sphinx-argparse = "==0.4.0"
|
|
129
129
|
pipreqs = "==0.4.13"
|
|
130
130
|
mdformat = "==0.7.18"
|
|
@@ -201,9 +201,6 @@ wrap-descriptions = 88
|
|
|
201
201
|
[tool.ruff]
|
|
202
202
|
target-version = "py39"
|
|
203
203
|
line-length = 88
|
|
204
|
-
select = ["D", "E", "F", "W", "B", "ISC", "C4", "UP"]
|
|
205
|
-
fixable = ["D", "E", "F", "W", "B", "ISC", "C4", "UP"]
|
|
206
|
-
ignore = ["B024", "B027", "D205", "D209"]
|
|
207
204
|
exclude = [
|
|
208
205
|
".bzr",
|
|
209
206
|
".direnv",
|
|
@@ -228,10 +225,15 @@ exclude = [
|
|
|
228
225
|
"proto",
|
|
229
226
|
]
|
|
230
227
|
|
|
231
|
-
[tool.ruff.
|
|
228
|
+
[tool.ruff.lint]
|
|
229
|
+
select = ["D", "E", "F", "W", "B", "ISC", "C4", "UP"]
|
|
230
|
+
fixable = ["D", "E", "F", "W", "B", "ISC", "C4", "UP"]
|
|
231
|
+
ignore = ["B024", "B027", "D205", "D209"]
|
|
232
|
+
|
|
233
|
+
[tool.ruff.lint.pydocstyle]
|
|
232
234
|
convention = "numpy"
|
|
233
235
|
|
|
234
|
-
[tool.ruff.per-file-ignores]
|
|
236
|
+
[tool.ruff.lint.per-file-ignores]
|
|
235
237
|
"src/py/flwr/server/strategy/*.py" = ["E501"]
|
|
236
238
|
|
|
237
239
|
[tool.docsig]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
"""Flower user auth plugins."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from flwr.common.auth_plugin import CliAuthPlugin
|
|
19
|
+
from flwr.common.constant import AuthType
|
|
20
|
+
|
|
21
|
+
from .oidc_cli_plugin import OidcCliPlugin
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_cli_auth_plugins() -> dict[str, type[CliAuthPlugin]]:
|
|
25
|
+
"""Return all CLI authentication plugins."""
|
|
26
|
+
return {AuthType.OIDC: OidcCliPlugin}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"get_cli_auth_plugins",
|
|
31
|
+
]
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
"""Flower CLI user auth plugin for OIDC."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import time
|
|
20
|
+
from collections.abc import Sequence
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any, Optional, Union
|
|
23
|
+
|
|
24
|
+
import typer
|
|
25
|
+
|
|
26
|
+
from flwr.common.auth_plugin import CliAuthPlugin
|
|
27
|
+
from flwr.common.constant import (
|
|
28
|
+
ACCESS_TOKEN_KEY,
|
|
29
|
+
AUTH_TYPE_KEY,
|
|
30
|
+
REFRESH_TOKEN_KEY,
|
|
31
|
+
AuthType,
|
|
32
|
+
)
|
|
33
|
+
from flwr.common.typing import UserAuthCredentials, UserAuthLoginDetails
|
|
34
|
+
from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
35
|
+
GetAuthTokensRequest,
|
|
36
|
+
GetAuthTokensResponse,
|
|
37
|
+
)
|
|
38
|
+
from flwr.proto.exec_pb2_grpc import ExecStub
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class OidcCliPlugin(CliAuthPlugin):
|
|
42
|
+
"""Flower OIDC auth plugin for CLI."""
|
|
43
|
+
|
|
44
|
+
def __init__(self, credentials_path: Path):
|
|
45
|
+
self.access_token: Optional[str] = None
|
|
46
|
+
self.refresh_token: Optional[str] = None
|
|
47
|
+
self.credentials_path = credentials_path
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def login(
|
|
51
|
+
login_details: UserAuthLoginDetails,
|
|
52
|
+
exec_stub: ExecStub,
|
|
53
|
+
) -> UserAuthCredentials:
|
|
54
|
+
"""Authenticate the user and retrieve authentication credentials."""
|
|
55
|
+
typer.secho(
|
|
56
|
+
"Please login with your user credentials here: "
|
|
57
|
+
f"{login_details.verification_uri_complete}",
|
|
58
|
+
fg=typer.colors.BLUE,
|
|
59
|
+
)
|
|
60
|
+
start_time = time.time()
|
|
61
|
+
time.sleep(login_details.interval)
|
|
62
|
+
|
|
63
|
+
while (time.time() - start_time) < login_details.expires_in:
|
|
64
|
+
res: GetAuthTokensResponse = exec_stub.GetAuthTokens(
|
|
65
|
+
GetAuthTokensRequest(device_code=login_details.device_code)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
access_token = res.access_token
|
|
69
|
+
refresh_token = res.refresh_token
|
|
70
|
+
|
|
71
|
+
if access_token and refresh_token:
|
|
72
|
+
typer.secho(
|
|
73
|
+
"✅ Login successful.",
|
|
74
|
+
fg=typer.colors.GREEN,
|
|
75
|
+
bold=False,
|
|
76
|
+
)
|
|
77
|
+
return UserAuthCredentials(
|
|
78
|
+
access_token=access_token,
|
|
79
|
+
refresh_token=refresh_token,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
time.sleep(login_details.interval)
|
|
83
|
+
|
|
84
|
+
typer.secho(
|
|
85
|
+
"❌ Timeout, failed to sign in.",
|
|
86
|
+
fg=typer.colors.RED,
|
|
87
|
+
bold=True,
|
|
88
|
+
)
|
|
89
|
+
raise typer.Exit(code=1)
|
|
90
|
+
|
|
91
|
+
def store_tokens(self, credentials: UserAuthCredentials) -> None:
|
|
92
|
+
"""Store authentication tokens to the `credentials_path`.
|
|
93
|
+
|
|
94
|
+
The credentials, including tokens, will be saved as a JSON file
|
|
95
|
+
at `credentials_path`.
|
|
96
|
+
"""
|
|
97
|
+
self.access_token = credentials.access_token
|
|
98
|
+
self.refresh_token = credentials.refresh_token
|
|
99
|
+
json_dict = {
|
|
100
|
+
AUTH_TYPE_KEY: AuthType.OIDC,
|
|
101
|
+
ACCESS_TOKEN_KEY: credentials.access_token,
|
|
102
|
+
REFRESH_TOKEN_KEY: credentials.refresh_token,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
with open(self.credentials_path, "w", encoding="utf-8") as file:
|
|
106
|
+
json.dump(json_dict, file, indent=4)
|
|
107
|
+
|
|
108
|
+
def load_tokens(self) -> None:
|
|
109
|
+
"""Load authentication tokens from the `credentials_path`."""
|
|
110
|
+
with open(self.credentials_path, encoding="utf-8") as file:
|
|
111
|
+
json_dict: dict[str, Any] = json.load(file)
|
|
112
|
+
access_token = json_dict.get(ACCESS_TOKEN_KEY)
|
|
113
|
+
refresh_token = json_dict.get(REFRESH_TOKEN_KEY)
|
|
114
|
+
|
|
115
|
+
if isinstance(access_token, str) and isinstance(refresh_token, str):
|
|
116
|
+
self.access_token = access_token
|
|
117
|
+
self.refresh_token = refresh_token
|
|
118
|
+
|
|
119
|
+
def write_tokens_to_metadata(
|
|
120
|
+
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
|
121
|
+
) -> Sequence[tuple[str, Union[str, bytes]]]:
|
|
122
|
+
"""Write authentication tokens to the provided metadata."""
|
|
123
|
+
if self.access_token is None or self.refresh_token is None:
|
|
124
|
+
typer.secho(
|
|
125
|
+
"❌ Missing authentication tokens. Please login first.",
|
|
126
|
+
fg=typer.colors.RED,
|
|
127
|
+
bold=True,
|
|
128
|
+
)
|
|
129
|
+
raise typer.Exit(code=1)
|
|
130
|
+
|
|
131
|
+
return list(metadata) + [
|
|
132
|
+
(ACCESS_TOKEN_KEY, self.access_token),
|
|
133
|
+
(REFRESH_TOKEN_KEY, self.refresh_token),
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
def read_tokens_from_metadata(
|
|
137
|
+
self, metadata: Sequence[tuple[str, Union[str, bytes]]]
|
|
138
|
+
) -> Optional[UserAuthCredentials]:
|
|
139
|
+
"""Read authentication tokens from the provided metadata."""
|
|
140
|
+
metadata_dict = dict(metadata)
|
|
141
|
+
access_token = metadata_dict.get(ACCESS_TOKEN_KEY)
|
|
142
|
+
refresh_token = metadata_dict.get(REFRESH_TOKEN_KEY)
|
|
143
|
+
|
|
144
|
+
if isinstance(access_token, str) and isinstance(refresh_token, str):
|
|
145
|
+
return UserAuthCredentials(
|
|
146
|
+
access_token=access_token,
|
|
147
|
+
refresh_token=refresh_token,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return None
|
|
@@ -54,8 +54,12 @@ class CliUserAuthInterceptor(
|
|
|
54
54
|
|
|
55
55
|
response = continuation(details, request)
|
|
56
56
|
if response.initial_metadata():
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
credentials = self.auth_plugin.read_tokens_from_metadata(
|
|
58
|
+
response.initial_metadata()
|
|
59
|
+
)
|
|
60
|
+
# The metadata contains tokens only if they have been refreshed
|
|
61
|
+
if credentials is not None:
|
|
62
|
+
self.auth_plugin.store_tokens(credentials)
|
|
59
63
|
|
|
60
64
|
return response
|
|
61
65
|
|
|
@@ -15,53 +15,19 @@
|
|
|
15
15
|
"""Utility to validate the `pyproject.toml` file."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
import zipfile
|
|
19
|
-
from io import BytesIO
|
|
20
18
|
from pathlib import Path
|
|
21
|
-
from typing import
|
|
19
|
+
from typing import Any, Optional, Union
|
|
22
20
|
|
|
23
21
|
import tomli
|
|
24
22
|
import typer
|
|
25
23
|
|
|
26
|
-
from flwr.common import
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Parameters
|
|
34
|
-
----------
|
|
35
|
-
fab_file : Union[Path, bytes]
|
|
36
|
-
The Flower App Bundle file to validate and extract the metadata from.
|
|
37
|
-
It can either be a path to the file or the file itself as bytes.
|
|
38
|
-
|
|
39
|
-
Returns
|
|
40
|
-
-------
|
|
41
|
-
Dict[str, Any]
|
|
42
|
-
The `config` of the given Flower App Bundle.
|
|
43
|
-
"""
|
|
44
|
-
fab_file_archive: Union[Path, IO[bytes]]
|
|
45
|
-
if isinstance(fab_file, bytes):
|
|
46
|
-
fab_file_archive = BytesIO(fab_file)
|
|
47
|
-
elif isinstance(fab_file, Path):
|
|
48
|
-
fab_file_archive = fab_file
|
|
49
|
-
else:
|
|
50
|
-
raise ValueError("fab_file must be either a Path or bytes")
|
|
51
|
-
|
|
52
|
-
with zipfile.ZipFile(fab_file_archive, "r") as zipf:
|
|
53
|
-
with zipf.open("pyproject.toml") as file:
|
|
54
|
-
toml_content = file.read().decode("utf-8")
|
|
55
|
-
|
|
56
|
-
conf = load_from_string(toml_content)
|
|
57
|
-
if conf is None:
|
|
58
|
-
raise ValueError("Invalid TOML content in pyproject.toml")
|
|
59
|
-
|
|
60
|
-
is_valid, errors, _ = validate(conf, check_module=False)
|
|
61
|
-
if not is_valid:
|
|
62
|
-
raise ValueError(errors)
|
|
63
|
-
|
|
64
|
-
return conf
|
|
24
|
+
from flwr.common.config import (
|
|
25
|
+
fuse_dicts,
|
|
26
|
+
get_fab_config,
|
|
27
|
+
get_metadata_from_config,
|
|
28
|
+
parse_config_args,
|
|
29
|
+
validate_config,
|
|
30
|
+
)
|
|
65
31
|
|
|
66
32
|
|
|
67
33
|
def get_fab_metadata(fab_file: Union[Path, bytes]) -> tuple[str, str]:
|
|
@@ -78,12 +44,7 @@ def get_fab_metadata(fab_file: Union[Path, bytes]) -> tuple[str, str]:
|
|
|
78
44
|
Tuple[str, str]
|
|
79
45
|
The `fab_id` and `fab_version` of the given Flower App Bundle.
|
|
80
46
|
"""
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return (
|
|
84
|
-
f"{conf['tool']['flwr']['app']['publisher']}/{conf['project']['name']}",
|
|
85
|
-
conf["project"]["version"],
|
|
86
|
-
)
|
|
47
|
+
return get_metadata_from_config(get_fab_config(fab_file))
|
|
87
48
|
|
|
88
49
|
|
|
89
50
|
def load_and_validate(
|
|
@@ -120,7 +81,7 @@ def load_and_validate(
|
|
|
120
81
|
]
|
|
121
82
|
return (None, errors, [])
|
|
122
83
|
|
|
123
|
-
is_valid, errors, warnings =
|
|
84
|
+
is_valid, errors, warnings = validate_config(config, check_module, path.parent)
|
|
124
85
|
|
|
125
86
|
if not is_valid:
|
|
126
87
|
return (None, errors, warnings)
|
|
@@ -133,102 +94,11 @@ def load(toml_path: Path) -> Optional[dict[str, Any]]:
|
|
|
133
94
|
if not toml_path.is_file():
|
|
134
95
|
return None
|
|
135
96
|
|
|
136
|
-
with toml_path.open(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
for key, value in config_dict.items():
|
|
142
|
-
if isinstance(value, dict):
|
|
143
|
-
_validate_run_config(config_dict[key], errors)
|
|
144
|
-
elif not isinstance(value, get_args(UserConfigValue)):
|
|
145
|
-
raise ValueError(
|
|
146
|
-
f"The value for key {key} needs to be of type `int`, `float`, "
|
|
147
|
-
"`bool, `str`, or a `dict` of those.",
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
# pylint: disable=too-many-branches
|
|
152
|
-
def validate_fields(config: dict[str, Any]) -> tuple[bool, list[str], list[str]]:
|
|
153
|
-
"""Validate pyproject.toml fields."""
|
|
154
|
-
errors = []
|
|
155
|
-
warnings = []
|
|
156
|
-
|
|
157
|
-
if "project" not in config:
|
|
158
|
-
errors.append("Missing [project] section")
|
|
159
|
-
else:
|
|
160
|
-
if "name" not in config["project"]:
|
|
161
|
-
errors.append('Property "name" missing in [project]')
|
|
162
|
-
if "version" not in config["project"]:
|
|
163
|
-
errors.append('Property "version" missing in [project]')
|
|
164
|
-
if "description" not in config["project"]:
|
|
165
|
-
warnings.append('Recommended property "description" missing in [project]')
|
|
166
|
-
if "license" not in config["project"]:
|
|
167
|
-
warnings.append('Recommended property "license" missing in [project]')
|
|
168
|
-
if "authors" not in config["project"]:
|
|
169
|
-
warnings.append('Recommended property "authors" missing in [project]')
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
"tool" not in config
|
|
173
|
-
or "flwr" not in config["tool"]
|
|
174
|
-
or "app" not in config["tool"]["flwr"]
|
|
175
|
-
):
|
|
176
|
-
errors.append("Missing [tool.flwr.app] section")
|
|
177
|
-
else:
|
|
178
|
-
if "publisher" not in config["tool"]["flwr"]["app"]:
|
|
179
|
-
errors.append('Property "publisher" missing in [tool.flwr.app]')
|
|
180
|
-
if "config" in config["tool"]["flwr"]["app"]:
|
|
181
|
-
_validate_run_config(config["tool"]["flwr"]["app"]["config"], errors)
|
|
182
|
-
if "components" not in config["tool"]["flwr"]["app"]:
|
|
183
|
-
errors.append("Missing [tool.flwr.app.components] section")
|
|
184
|
-
else:
|
|
185
|
-
if "serverapp" not in config["tool"]["flwr"]["app"]["components"]:
|
|
186
|
-
errors.append(
|
|
187
|
-
'Property "serverapp" missing in [tool.flwr.app.components]'
|
|
188
|
-
)
|
|
189
|
-
if "clientapp" not in config["tool"]["flwr"]["app"]["components"]:
|
|
190
|
-
errors.append(
|
|
191
|
-
'Property "clientapp" missing in [tool.flwr.app.components]'
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
return len(errors) == 0, errors, warnings
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
def validate(
|
|
198
|
-
config: dict[str, Any],
|
|
199
|
-
check_module: bool = True,
|
|
200
|
-
project_dir: Optional[Union[str, Path]] = None,
|
|
201
|
-
) -> tuple[bool, list[str], list[str]]:
|
|
202
|
-
"""Validate pyproject.toml."""
|
|
203
|
-
is_valid, errors, warnings = validate_fields(config)
|
|
204
|
-
|
|
205
|
-
if not is_valid:
|
|
206
|
-
return False, errors, warnings
|
|
207
|
-
|
|
208
|
-
# Validate serverapp
|
|
209
|
-
serverapp_ref = config["tool"]["flwr"]["app"]["components"]["serverapp"]
|
|
210
|
-
is_valid, reason = object_ref.validate(serverapp_ref, check_module, project_dir)
|
|
211
|
-
|
|
212
|
-
if not is_valid and isinstance(reason, str):
|
|
213
|
-
return False, [reason], []
|
|
214
|
-
|
|
215
|
-
# Validate clientapp
|
|
216
|
-
clientapp_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
|
|
217
|
-
is_valid, reason = object_ref.validate(clientapp_ref, check_module, project_dir)
|
|
218
|
-
|
|
219
|
-
if not is_valid and isinstance(reason, str):
|
|
220
|
-
return False, [reason], []
|
|
221
|
-
|
|
222
|
-
return True, [], []
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
def load_from_string(toml_content: str) -> Optional[dict[str, Any]]:
|
|
226
|
-
"""Load TOML content from a string and return as dict."""
|
|
227
|
-
try:
|
|
228
|
-
data = tomli.loads(toml_content)
|
|
229
|
-
return data
|
|
230
|
-
except tomli.TOMLDecodeError:
|
|
231
|
-
return None
|
|
97
|
+
with toml_path.open("rb") as toml_file:
|
|
98
|
+
try:
|
|
99
|
+
return tomli.load(toml_file)
|
|
100
|
+
except tomli.TOMLDecodeError:
|
|
101
|
+
return None
|
|
232
102
|
|
|
233
103
|
|
|
234
104
|
def process_loaded_project_config(
|
|
@@ -263,7 +133,9 @@ def process_loaded_project_config(
|
|
|
263
133
|
|
|
264
134
|
|
|
265
135
|
def validate_federation_in_project_config(
|
|
266
|
-
federation: Optional[str],
|
|
136
|
+
federation: Optional[str],
|
|
137
|
+
config: dict[str, Any],
|
|
138
|
+
overrides: Optional[list[str]] = None,
|
|
267
139
|
) -> tuple[str, dict[str, Any]]:
|
|
268
140
|
"""Validate the federation name in the Flower project configuration."""
|
|
269
141
|
federation = federation or config["tool"]["flwr"]["federations"].get("default")
|
|
@@ -293,6 +165,11 @@ def validate_federation_in_project_config(
|
|
|
293
165
|
)
|
|
294
166
|
raise typer.Exit(code=1)
|
|
295
167
|
|
|
168
|
+
# Override the federation configuration if provided
|
|
169
|
+
if overrides:
|
|
170
|
+
overrides_dict = parse_config_args(overrides, flatten=False)
|
|
171
|
+
federation_config = fuse_dicts(federation_config, overrides_dict)
|
|
172
|
+
|
|
296
173
|
return federation, federation_config
|
|
297
174
|
|
|
298
175
|
|
|
@@ -305,7 +182,7 @@ def validate_certificate_in_federation_config(
|
|
|
305
182
|
root_certificates_bytes = (app / root_certificates).read_bytes()
|
|
306
183
|
if insecure := bool(insecure_str):
|
|
307
184
|
typer.secho(
|
|
308
|
-
"❌ `
|
|
185
|
+
"❌ `root-certificates` were provided but the `insecure` parameter "
|
|
309
186
|
"is set to `True`.",
|
|
310
187
|
fg=typer.colors.RED,
|
|
311
188
|
bold=True,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
"""Constants for CLI commands."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# The help message for `--federation-config` option
|
|
19
|
+
FEDERATION_CONFIG_HELP_MESSAGE = (
|
|
20
|
+
"Override federation configuration values in the format:\n\n"
|
|
21
|
+
"`--federation-config 'key1=value1 key2=value2' --federation-config "
|
|
22
|
+
"'key3=value3'`\n\nValues can be of any type supported in TOML, such as "
|
|
23
|
+
"bool, int, float, or string. Ensure that the keys (`key1`, `key2`, `key3` "
|
|
24
|
+
"in this example) exist in the federation configuration under the "
|
|
25
|
+
"`[tool.flwr.federations.<YOUR_FEDERATION>]` table of the `pyproject.toml` "
|
|
26
|
+
"for proper overriding."
|
|
27
|
+
)
|
|
@@ -154,7 +154,7 @@ def validate_and_install(
|
|
|
154
154
|
)
|
|
155
155
|
raise typer.Exit(code=1)
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
fab_id, version = get_metadata_from_config(config)
|
|
158
158
|
publisher, project_name = fab_id.split("/")
|
|
159
159
|
config_metadata = (publisher, project_name, version, fab_hash)
|
|
160
160
|
|