flwr 1.17.0__tar.gz → 1.19.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.17.0 → flwr-1.19.0}/PKG-INFO +6 -5
- {flwr-1.17.0 → flwr-1.19.0}/README.md +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/__init__.py +1 -1
- flwr-1.19.0/py/flwr/app/__init__.py +15 -0
- flwr-1.19.0/py/flwr/app/error.py +68 -0
- flwr-1.19.0/py/flwr/app/metadata.py +223 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/app.py +21 -2
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/build.py +83 -58
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/cli_user_auth_interceptor.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/config_utils.py +53 -17
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/example.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/install.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/log.py +4 -4
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/login/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/login/login.py +15 -8
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/ls.py +16 -37
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/new.py +4 -4
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +4 -4
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/model.baseline.py.tpl +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.baseline.py.tpl +2 -3
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.sklearn.py.tpl +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -17
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/run/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/run/run.py +11 -19
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/stop.py +3 -3
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/utils.py +42 -17
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/__init__.py +3 -3
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/client.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/client_app.py +140 -138
- flwr-1.19.0/py/flwr/client/clientapp/__init__.py +15 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/clientapp/utils.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/dpfedavg_numpy_client.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/grpc_adapter_client/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/grpc_adapter_client/connection.py +5 -5
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/grpc_rere_client/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/grpc_rere_client/client_interceptor.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/grpc_rere_client/connection.py +131 -61
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/grpc_rere_client/grpc_adapter.py +35 -7
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/message_handler/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/message_handler/message_handler.py +2 -2
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/mod/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/mod/centraldp_mods.py +1 -1
- flwr-1.19.0/py/flwr/client/mod/comms_mods.py +93 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/mod/localdp_mod.py +6 -6
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/mod/secure_aggregation/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/mod/secure_aggregation/secagg_mod.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/mod/utils.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/numpy_client.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/rest_client/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/rest_client/connection.py +174 -68
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/run_info_store.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/client/typing.py +1 -1
- flwr-1.19.0/py/flwr/clientapp/__init__.py +15 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/__init__.py +3 -3
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/address.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/args.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/auth_plugin/__init__.py +3 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/auth_plugin/auth_plugin.py +30 -4
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/config.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/constant.py +37 -8
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/context.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/date.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/differential_privacy.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/differential_privacy_constants.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/dp.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/event_log_plugin/event_log_plugin.py +3 -3
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/exit/exit.py +6 -6
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/exit_handlers.py +31 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/grpc.py +1 -1
- flwr-1.19.0/py/flwr/common/heartbeat.py +165 -0
- flwr-1.19.0/py/flwr/common/inflatable.py +290 -0
- flwr-1.19.0/py/flwr/common/inflatable_grpc_utils.py +99 -0
- flwr-1.19.0/py/flwr/common/inflatable_rest_utils.py +99 -0
- flwr-1.19.0/py/flwr/common/inflatable_utils.py +341 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/logger.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/message.py +137 -252
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/object_ref.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/parameter.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/pyproject.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/record/__init__.py +3 -2
- flwr-1.19.0/py/flwr/common/record/array.py +323 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/record/arrayrecord.py +121 -243
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/record/configrecord.py +71 -16
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/record/conversion_utils.py +2 -2
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/record/metricrecord.py +71 -20
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/record/recorddict.py +207 -90
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/record/typeddict.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/recorddict_compat.py +2 -2
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/retry_invoker.py +15 -11
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/secure_aggregation/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/secure_aggregation/crypto/__init__.py +1 -1
- flwr-1.19.0/py/flwr/common/secure_aggregation/crypto/shamir.py +97 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/secure_aggregation/quantization.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/serde.py +60 -184
- flwr-1.19.0/py/flwr/common/serde_utils.py +175 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/telemetry.py +2 -2
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/typing.py +6 -4
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/version.py +1 -1
- flwr-1.19.0/py/flwr/compat/__init__.py +15 -0
- flwr-1.19.0/py/flwr/compat/client/__init__.py +15 -0
- {flwr-1.17.0/src/py/flwr → flwr-1.19.0/py/flwr/compat}/client/app.py +71 -211
- {flwr-1.17.0/src/py/flwr → flwr-1.19.0/py/flwr/compat}/client/grpc_client/__init__.py +1 -1
- {flwr-1.17.0/src/py/flwr → flwr-1.19.0/py/flwr/compat}/client/grpc_client/connection.py +13 -13
- flwr-1.19.0/py/flwr/compat/common/__init__.py +15 -0
- flwr-1.19.0/py/flwr/compat/server/__init__.py +15 -0
- flwr-1.19.0/py/flwr/compat/server/app.py +174 -0
- flwr-1.19.0/py/flwr/compat/simulation/__init__.py +15 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/__init__.py +1 -1
- flwr-1.19.0/py/flwr/proto/fleet_pb2.py +61 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/fleet_pb2.pyi +49 -35
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/fleet_pb2_grpc.py +117 -13
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/fleet_pb2_grpc.pyi +47 -6
- flwr-1.19.0/py/flwr/proto/heartbeat_pb2.py +33 -0
- flwr-1.19.0/py/flwr/proto/heartbeat_pb2.pyi +66 -0
- flwr-1.19.0/py/flwr/proto/message_pb2.py +58 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/message_pb2.pyi +125 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/recorddict_pb2.py +16 -28
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/recorddict_pb2.pyi +46 -64
- flwr-1.19.0/py/flwr/proto/run_pb2.py +56 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/run_pb2.pyi +4 -52
- flwr-1.19.0/py/flwr/proto/run_pb2_grpc.py +4 -0
- flwr-1.19.0/py/flwr/proto/run_pb2_grpc.pyi +4 -0
- flwr-1.19.0/py/flwr/proto/serverappio_pb2.py +60 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/serverappio_pb2.pyi +45 -3
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/serverappio_pb2_grpc.py +138 -34
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/serverappio_pb2_grpc.pyi +54 -13
- flwr-1.19.0/py/flwr/proto/simulationio_pb2.py +39 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/simulationio_pb2_grpc.py +35 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/simulationio_pb2_grpc.pyi +14 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/__init__.py +2 -2
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/app.py +69 -187
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/client_manager.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/client_proxy.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/compat/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/compat/app.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/compat/app_utils.py +51 -29
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/compat/legacy_context.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/criterion.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/fleet_event_log_interceptor.py +2 -2
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/grid/grid.py +3 -3
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/grid/grpc_grid.py +104 -34
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/grid/inmemory_grid.py +5 -4
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/history.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/run_serverapp.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/server.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/server_app.py +65 -58
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/server_config.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/serverapp/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/serverapp/app.py +19 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/serverapp_components.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/aggregate.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/bulyan.py +2 -2
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/dp_adaptive_clipping.py +17 -17
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/dp_fixed_clipping.py +17 -17
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/dpfedavg_adaptive.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/dpfedavg_fixed.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fault_tolerant_fedavg.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedadagrad.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedadam.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedavg.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedavg_android.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedavgm.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedmedian.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedopt.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedprox.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedtrimmedavg.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedxgb_bagging.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedxgb_cyclic.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedxgb_nn_avg.py +3 -2
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/fedyogi.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/krum.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/qfedavg.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/strategy/strategy.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/ffs/__init__.py +3 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/ffs/disk_ffs.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/ffs/ffs.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/ffs/ffs_factory.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_adapter/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +14 -4
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +13 -13
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +102 -8
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
- flwr-1.19.0/py/flwr/server/superlink/fleet/message_handler/message_handler.py +292 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/rest_rere/rest_api.py +73 -12
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/vce/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/fleet/vce/vce_api.py +7 -4
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/linkstate/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/linkstate/in_memory_linkstate.py +139 -44
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/linkstate/linkstate.py +54 -21
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/linkstate/linkstate_factory.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/linkstate/sqlite_linkstate.py +150 -56
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/linkstate/utils.py +34 -30
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/serverappio/serverappio_grpc.py +3 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/serverappio/serverappio_servicer.py +211 -57
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/simulation/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/simulation/simulationio_servicer.py +26 -2
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/utils.py +45 -3
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/typing.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/utils/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/utils/tensorboard.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/utils/validator.py +3 -3
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/workflow/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/workflow/constant.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/workflow/default_workflows.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/workflow/secure_aggregation/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
- flwr-1.19.0/py/flwr/serverapp/__init__.py +15 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/simulation/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/simulation/app.py +18 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/simulation/legacy_app.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/simulation/ray_transport/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/simulation/ray_transport/ray_actor.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/simulation/ray_transport/utils.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/simulation/run_simulation.py +2 -2
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/simulation/simulationio_connection.py +1 -1
- flwr-1.19.0/py/flwr/supercore/__init__.py +15 -0
- flwr-1.19.0/py/flwr/supercore/object_store/__init__.py +24 -0
- flwr-1.19.0/py/flwr/supercore/object_store/in_memory_object_store.py +229 -0
- flwr-1.19.0/py/flwr/supercore/object_store/object_store.py +192 -0
- flwr-1.19.0/py/flwr/supercore/object_store/object_store_factory.py +44 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/superexec/__init__.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/superexec/app.py +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/superexec/deployment.py +7 -3
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/superexec/exec_event_log_interceptor.py +4 -4
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/superexec/exec_grpc.py +8 -4
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/superexec/exec_servicer.py +126 -24
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/superexec/exec_user_auth_interceptor.py +38 -9
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/superexec/executor.py +5 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/superexec/simulation.py +8 -2
- flwr-1.19.0/py/flwr/superlink/__init__.py +15 -0
- flwr-1.19.0/py/flwr/supernode/__init__.py +15 -0
- flwr-1.19.0/py/flwr/supernode/cli/__init__.py +24 -0
- flwr-1.17.0/src/py/flwr/client/supernode/app.py → flwr-1.19.0/py/flwr/supernode/cli/flower_supernode.py +4 -13
- flwr-1.19.0/py/flwr/supernode/cli/flwr_clientapp.py +81 -0
- {flwr-1.17.0/src/py/flwr/client → flwr-1.19.0/py/flwr/supernode}/nodestate/__init__.py +1 -1
- flwr-1.19.0/py/flwr/supernode/nodestate/in_memory_nodestate.py +190 -0
- flwr-1.19.0/py/flwr/supernode/nodestate/nodestate.py +212 -0
- {flwr-1.17.0/src/py/flwr/client → flwr-1.19.0/py/flwr/supernode}/nodestate/nodestate_factory.py +1 -1
- flwr-1.19.0/py/flwr/supernode/runtime/__init__.py +15 -0
- flwr-1.17.0/src/py/flwr/client/clientapp/app.py → flwr-1.19.0/py/flwr/supernode/runtime/run_clientapp.py +26 -57
- flwr-1.19.0/py/flwr/supernode/servicer/__init__.py +15 -0
- flwr-1.19.0/py/flwr/supernode/servicer/clientappio/__init__.py +24 -0
- {flwr-1.17.0/src/py/flwr/client/clientapp → flwr-1.19.0/py/flwr/supernode/servicer/clientappio}/clientappio_servicer.py +1 -1
- flwr-1.19.0/py/flwr/supernode/start_client_internal.py +491 -0
- {flwr-1.17.0 → flwr-1.19.0}/pyproject.toml +15 -18
- flwr-1.17.0/LICENSE +0 -202
- flwr-1.17.0/src/py/flwr/client/clientapp/__init__.py +0 -22
- flwr-1.17.0/src/py/flwr/client/heartbeat.py +0 -74
- flwr-1.17.0/src/py/flwr/client/mod/comms_mods.py +0 -74
- flwr-1.17.0/src/py/flwr/client/nodestate/in_memory_nodestate.py +0 -38
- flwr-1.17.0/src/py/flwr/client/nodestate/nodestate.py +0 -31
- flwr-1.17.0/src/py/flwr/client/supernode/__init__.py +0 -22
- flwr-1.17.0/src/py/flwr/common/secure_aggregation/crypto/shamir.py +0 -75
- flwr-1.17.0/src/py/flwr/proto/fleet_pb2.py +0 -56
- flwr-1.17.0/src/py/flwr/proto/message_pb2.py +0 -41
- flwr-1.17.0/src/py/flwr/proto/run_pb2.py +0 -64
- flwr-1.17.0/src/py/flwr/proto/serverappio_pb2.py +0 -51
- flwr-1.17.0/src/py/flwr/proto/simulationio_pb2.py +0 -38
- flwr-1.17.0/src/py/flwr/server/superlink/fleet/message_handler/message_handler.py +0 -175
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/auth_plugin/__init__.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/auth_plugin/oidc_cli_plugin.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/constant.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/.gitignore.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/LICENSE.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/README.baseline.md.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/README.md.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/__init__.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.baseline.py.tpl +1 -1
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/event_log_plugin/__init__.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/exit/__init__.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/common/exit/exit_code.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/clientappio_pb2.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/clientappio_pb2.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/clientappio_pb2_grpc.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/clientappio_pb2_grpc.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/error_pb2.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/error_pb2.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/error_pb2_grpc.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/error_pb2_grpc.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/exec_pb2.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/exec_pb2.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/exec_pb2_grpc.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/exec_pb2_grpc.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/fab_pb2.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/fab_pb2.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/fab_pb2_grpc.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/fab_pb2_grpc.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/grpcadapter_pb2.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/grpcadapter_pb2.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/grpcadapter_pb2_grpc.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/grpcadapter_pb2_grpc.pyi +0 -0
- /flwr-1.17.0/src/py/flwr/proto/log_pb2_grpc.py → /flwr-1.19.0/py/flwr/proto/heartbeat_pb2_grpc.py +0 -0
- /flwr-1.17.0/src/py/flwr/proto/log_pb2_grpc.pyi → /flwr-1.19.0/py/flwr/proto/heartbeat_pb2_grpc.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/log_pb2.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/log_pb2.pyi +0 -0
- /flwr-1.17.0/src/py/flwr/proto/message_pb2_grpc.py → /flwr-1.19.0/py/flwr/proto/log_pb2_grpc.py +0 -0
- /flwr-1.17.0/src/py/flwr/proto/message_pb2_grpc.pyi → /flwr-1.19.0/py/flwr/proto/log_pb2_grpc.pyi +0 -0
- /flwr-1.17.0/src/py/flwr/proto/node_pb2_grpc.py → /flwr-1.19.0/py/flwr/proto/message_pb2_grpc.py +0 -0
- /flwr-1.17.0/src/py/flwr/proto/node_pb2_grpc.pyi → /flwr-1.19.0/py/flwr/proto/message_pb2_grpc.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/node_pb2.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/node_pb2.pyi +0 -0
- /flwr-1.17.0/src/py/flwr/proto/recorddict_pb2_grpc.py → /flwr-1.19.0/py/flwr/proto/node_pb2_grpc.py +0 -0
- /flwr-1.17.0/src/py/flwr/proto/recorddict_pb2_grpc.pyi → /flwr-1.19.0/py/flwr/proto/node_pb2_grpc.pyi +0 -0
- /flwr-1.17.0/src/py/flwr/proto/run_pb2_grpc.py → /flwr-1.19.0/py/flwr/proto/recorddict_pb2_grpc.py +0 -0
- /flwr-1.17.0/src/py/flwr/proto/run_pb2_grpc.pyi → /flwr-1.19.0/py/flwr/proto/recorddict_pb2_grpc.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/simulationio_pb2.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/transport_pb2.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/transport_pb2.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/transport_pb2_grpc.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/proto/transport_pb2_grpc.pyi +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/py.typed +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/compat/grid_client_proxy.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/grid/__init__.py +0 -0
- {flwr-1.17.0/src → flwr-1.19.0}/py/flwr/server/superlink/serverappio/__init__.py +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: flwr
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.19.0
|
|
4
4
|
Summary: Flower: A Friendly Federated AI Framework
|
|
5
|
-
Home-page: https://flower.ai
|
|
6
5
|
License: Apache-2.0
|
|
7
6
|
Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
|
|
8
7
|
Author: The Flower Authors
|
|
@@ -19,8 +18,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
24
23
|
Classifier: Programming Language :: Python :: 3.9
|
|
25
24
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
26
25
|
Classifier: Topic :: Scientific/Engineering
|
|
@@ -32,6 +31,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
32
31
|
Classifier: Typing :: Typed
|
|
33
32
|
Provides-Extra: rest
|
|
34
33
|
Provides-Extra: simulation
|
|
34
|
+
Requires-Dist: click (<8.2.0)
|
|
35
35
|
Requires-Dist: cryptography (>=44.0.1,<45.0.0)
|
|
36
36
|
Requires-Dist: grpcio (>=1.62.3,<2.0.0,!=1.65.0)
|
|
37
37
|
Requires-Dist: iterators (>=0.0.2,<0.0.3)
|
|
@@ -49,6 +49,7 @@ Requires-Dist: tomli-w (>=1.0.0,<2.0.0)
|
|
|
49
49
|
Requires-Dist: typer (>=0.12.5,<0.13.0)
|
|
50
50
|
Requires-Dist: uvicorn[standard] (>=0.34.0,<0.35.0) ; extra == "rest"
|
|
51
51
|
Project-URL: Documentation, https://flower.ai
|
|
52
|
+
Project-URL: Homepage, https://flower.ai
|
|
52
53
|
Project-URL: Repository, https://github.com/adap/flower
|
|
53
54
|
Description-Content-Type: text/markdown
|
|
54
55
|
|
|
@@ -56,7 +57,7 @@ Description-Content-Type: text/markdown
|
|
|
56
57
|
|
|
57
58
|
<p align="center">
|
|
58
59
|
<a href="https://flower.ai/">
|
|
59
|
-
<img src="https://flower.ai/_next/image/?url=%2F_next%2Fstatic%2Fmedia%
|
|
60
|
+
<img src="https://flower.ai/_next/image/?url=%2F_next%2Fstatic%2Fmedia%2Fflwr-head.4d68867a.png&w=384&q=75" width="140px" alt="Flower Website" />
|
|
60
61
|
</a>
|
|
61
62
|
</p>
|
|
62
63
|
<p align="center">
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<a href="https://flower.ai/">
|
|
5
|
-
<img src="https://flower.ai/_next/image/?url=%2F_next%2Fstatic%2Fmedia%
|
|
5
|
+
<img src="https://flower.ai/_next/image/?url=%2F_next%2Fstatic%2Fmedia%2Fflwr-head.4d68867a.png&w=384&q=75" width="140px" alt="Flower Website" />
|
|
6
6
|
</a>
|
|
7
7
|
</p>
|
|
8
8
|
<p align="center">
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
"""Public Flower App APIs."""
|
|
@@ -0,0 +1,68 @@
|
|
|
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
|
+
"""Error."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from typing import Optional, cast
|
|
21
|
+
|
|
22
|
+
DEFAULT_TTL = 43200 # This is 12 hours
|
|
23
|
+
MESSAGE_INIT_ERROR_MESSAGE = (
|
|
24
|
+
"Invalid arguments for Message. Expected one of the documented "
|
|
25
|
+
"signatures: Message(content: RecordDict, dst_node_id: int, message_type: str,"
|
|
26
|
+
" *, [ttl: float, group_id: str]) or Message(content: RecordDict | error: Error,"
|
|
27
|
+
" *, reply_to: Message, [ttl: float])."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Error:
|
|
32
|
+
"""The class storing information about an error that occurred.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
code : int
|
|
37
|
+
An identifier for the error.
|
|
38
|
+
reason : Optional[str]
|
|
39
|
+
A reason for why the error arose (e.g. an exception stack-trace)
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, code: int, reason: str | None = None) -> None:
|
|
43
|
+
var_dict = {
|
|
44
|
+
"_code": code,
|
|
45
|
+
"_reason": reason,
|
|
46
|
+
}
|
|
47
|
+
self.__dict__.update(var_dict)
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def code(self) -> int:
|
|
51
|
+
"""Error code."""
|
|
52
|
+
return cast(int, self.__dict__["_code"])
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def reason(self) -> str | None:
|
|
56
|
+
"""Reason reported about the error."""
|
|
57
|
+
return cast(Optional[str], self.__dict__["_reason"])
|
|
58
|
+
|
|
59
|
+
def __repr__(self) -> str:
|
|
60
|
+
"""Return a string representation of this instance."""
|
|
61
|
+
view = ", ".join([f"{k.lstrip('_')}={v!r}" for k, v in self.__dict__.items()])
|
|
62
|
+
return f"{self.__class__.__qualname__}({view})"
|
|
63
|
+
|
|
64
|
+
def __eq__(self, other: object) -> bool:
|
|
65
|
+
"""Compare two instances of the class."""
|
|
66
|
+
if not isinstance(other, self.__class__):
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
return self.__dict__ == other.__dict__
|
|
@@ -0,0 +1,223 @@
|
|
|
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
|
+
"""Metadata."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from typing import cast
|
|
21
|
+
|
|
22
|
+
from ..common.constant import MessageType, MessageTypeLegacy
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Metadata: # pylint: disable=too-many-instance-attributes
|
|
26
|
+
"""The class representing metadata associated with the current message.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
run_id : int
|
|
31
|
+
An identifier for the current run.
|
|
32
|
+
message_id : str
|
|
33
|
+
An identifier for the current message.
|
|
34
|
+
src_node_id : int
|
|
35
|
+
An identifier for the node sending this message.
|
|
36
|
+
dst_node_id : int
|
|
37
|
+
An identifier for the node receiving this message.
|
|
38
|
+
reply_to_message_id : str
|
|
39
|
+
An identifier for the message to which this message is a reply.
|
|
40
|
+
group_id : str
|
|
41
|
+
An identifier for grouping messages. In some settings,
|
|
42
|
+
this is used as the FL round.
|
|
43
|
+
created_at : float
|
|
44
|
+
Unix timestamp when the message was created.
|
|
45
|
+
ttl : float
|
|
46
|
+
Time-to-live for this message in seconds.
|
|
47
|
+
message_type : str
|
|
48
|
+
A string that encodes the action to be executed on
|
|
49
|
+
the receiving end.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
53
|
+
self,
|
|
54
|
+
run_id: int,
|
|
55
|
+
message_id: str,
|
|
56
|
+
src_node_id: int,
|
|
57
|
+
dst_node_id: int,
|
|
58
|
+
reply_to_message_id: str,
|
|
59
|
+
group_id: str,
|
|
60
|
+
created_at: float,
|
|
61
|
+
ttl: float,
|
|
62
|
+
message_type: str,
|
|
63
|
+
) -> None:
|
|
64
|
+
var_dict = {
|
|
65
|
+
"_run_id": run_id,
|
|
66
|
+
"_message_id": message_id,
|
|
67
|
+
"_src_node_id": src_node_id,
|
|
68
|
+
"_dst_node_id": dst_node_id,
|
|
69
|
+
"_reply_to_message_id": reply_to_message_id,
|
|
70
|
+
"_group_id": group_id,
|
|
71
|
+
"_created_at": created_at,
|
|
72
|
+
"_ttl": ttl,
|
|
73
|
+
"_message_type": message_type,
|
|
74
|
+
}
|
|
75
|
+
self.__dict__.update(var_dict)
|
|
76
|
+
self.message_type = message_type # Trigger validation
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def run_id(self) -> int:
|
|
80
|
+
"""An identifier for the current run."""
|
|
81
|
+
return cast(int, self.__dict__["_run_id"])
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def message_id(self) -> str:
|
|
85
|
+
"""An identifier for the current message."""
|
|
86
|
+
return cast(str, self.__dict__["_message_id"])
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def src_node_id(self) -> int:
|
|
90
|
+
"""An identifier for the node sending this message."""
|
|
91
|
+
return cast(int, self.__dict__["_src_node_id"])
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def reply_to_message_id(self) -> str:
|
|
95
|
+
"""An identifier for the message to which this message is a reply."""
|
|
96
|
+
return cast(str, self.__dict__["_reply_to_message_id"])
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def dst_node_id(self) -> int:
|
|
100
|
+
"""An identifier for the node receiving this message."""
|
|
101
|
+
return cast(int, self.__dict__["_dst_node_id"])
|
|
102
|
+
|
|
103
|
+
@dst_node_id.setter
|
|
104
|
+
def dst_node_id(self, value: int) -> None:
|
|
105
|
+
"""Set dst_node_id."""
|
|
106
|
+
self.__dict__["_dst_node_id"] = value
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def group_id(self) -> str:
|
|
110
|
+
"""An identifier for grouping messages."""
|
|
111
|
+
return cast(str, self.__dict__["_group_id"])
|
|
112
|
+
|
|
113
|
+
@group_id.setter
|
|
114
|
+
def group_id(self, value: str) -> None:
|
|
115
|
+
"""Set group_id."""
|
|
116
|
+
self.__dict__["_group_id"] = value
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def created_at(self) -> float:
|
|
120
|
+
"""Unix timestamp when the message was created."""
|
|
121
|
+
return cast(float, self.__dict__["_created_at"])
|
|
122
|
+
|
|
123
|
+
@created_at.setter
|
|
124
|
+
def created_at(self, value: float) -> None:
|
|
125
|
+
"""Set creation timestamp of this message."""
|
|
126
|
+
self.__dict__["_created_at"] = value
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def delivered_at(self) -> str:
|
|
130
|
+
"""Unix timestamp when the message was delivered."""
|
|
131
|
+
return cast(str, self.__dict__["_delivered_at"])
|
|
132
|
+
|
|
133
|
+
@delivered_at.setter
|
|
134
|
+
def delivered_at(self, value: str) -> None:
|
|
135
|
+
"""Set delivery timestamp of this message."""
|
|
136
|
+
self.__dict__["_delivered_at"] = value
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def ttl(self) -> float:
|
|
140
|
+
"""Time-to-live for this message."""
|
|
141
|
+
return cast(float, self.__dict__["_ttl"])
|
|
142
|
+
|
|
143
|
+
@ttl.setter
|
|
144
|
+
def ttl(self, value: float) -> None:
|
|
145
|
+
"""Set ttl."""
|
|
146
|
+
self.__dict__["_ttl"] = value
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def message_type(self) -> str:
|
|
150
|
+
"""A string that encodes the action to be executed on the receiving end."""
|
|
151
|
+
return cast(str, self.__dict__["_message_type"])
|
|
152
|
+
|
|
153
|
+
@message_type.setter
|
|
154
|
+
def message_type(self, value: str) -> None:
|
|
155
|
+
"""Set message_type."""
|
|
156
|
+
# Validate message type
|
|
157
|
+
if validate_legacy_message_type(value):
|
|
158
|
+
pass # Backward compatibility for legacy message types
|
|
159
|
+
elif not validate_message_type(value):
|
|
160
|
+
raise ValueError(
|
|
161
|
+
f"Invalid message type: '{value}'. "
|
|
162
|
+
"Expected format: '<category>' or '<category>.<action>', "
|
|
163
|
+
"where <category> must be 'train', 'evaluate', or 'query', "
|
|
164
|
+
"and <action> must be a valid Python identifier."
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
self.__dict__["_message_type"] = value
|
|
168
|
+
|
|
169
|
+
def __repr__(self) -> str:
|
|
170
|
+
"""Return a string representation of this instance."""
|
|
171
|
+
view = ", ".join([f"{k.lstrip('_')}={v!r}" for k, v in self.__dict__.items()])
|
|
172
|
+
return f"{self.__class__.__qualname__}({view})"
|
|
173
|
+
|
|
174
|
+
def __eq__(self, other: object) -> bool:
|
|
175
|
+
"""Compare two instances of the class."""
|
|
176
|
+
if not isinstance(other, self.__class__):
|
|
177
|
+
raise NotImplementedError
|
|
178
|
+
return self.__dict__ == other.__dict__
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def validate_message_type(message_type: str) -> bool:
|
|
182
|
+
"""Validate if the message type is valid.
|
|
183
|
+
|
|
184
|
+
A valid message type format must be one of the following:
|
|
185
|
+
|
|
186
|
+
- "<category>"
|
|
187
|
+
- "<category>.<action>"
|
|
188
|
+
|
|
189
|
+
where `category` must be one of "train", "evaluate", or "query",
|
|
190
|
+
and `action` must be a valid Python identifier.
|
|
191
|
+
"""
|
|
192
|
+
# Check if conforming to the format "<category>"
|
|
193
|
+
valid_types = {
|
|
194
|
+
MessageType.TRAIN,
|
|
195
|
+
MessageType.EVALUATE,
|
|
196
|
+
MessageType.QUERY,
|
|
197
|
+
MessageType.SYSTEM,
|
|
198
|
+
}
|
|
199
|
+
if message_type in valid_types:
|
|
200
|
+
return True
|
|
201
|
+
|
|
202
|
+
# Check if conforming to the format "<category>.<action>"
|
|
203
|
+
if message_type.count(".") != 1:
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
category, action = message_type.split(".")
|
|
207
|
+
if category in valid_types and action.isidentifier():
|
|
208
|
+
return True
|
|
209
|
+
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def validate_legacy_message_type(message_type: str) -> bool:
|
|
214
|
+
"""Validate if the legacy message type is valid."""
|
|
215
|
+
# Backward compatibility for legacy message types
|
|
216
|
+
if message_type in (
|
|
217
|
+
MessageTypeLegacy.GET_PARAMETERS,
|
|
218
|
+
MessageTypeLegacy.GET_PROPERTIES,
|
|
219
|
+
"reconnect",
|
|
220
|
+
):
|
|
221
|
+
return True
|
|
222
|
+
|
|
223
|
+
return False
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
# ==============================================================================
|
|
15
15
|
"""Flower command line interface."""
|
|
16
16
|
|
|
17
|
-
|
|
18
17
|
import typer
|
|
19
18
|
from typer.main import get_command
|
|
20
19
|
|
|
20
|
+
from flwr.common.version import package_version
|
|
21
|
+
|
|
21
22
|
from .build import build
|
|
22
23
|
from .install import install
|
|
23
24
|
from .log import log
|
|
@@ -34,6 +35,7 @@ app = typer.Typer(
|
|
|
34
35
|
bold=True,
|
|
35
36
|
),
|
|
36
37
|
no_args_is_help=True,
|
|
38
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
|
37
39
|
)
|
|
38
40
|
|
|
39
41
|
app.command()(new)
|
|
@@ -47,5 +49,22 @@ app.command()(login)
|
|
|
47
49
|
|
|
48
50
|
typer_click_object = get_command(app)
|
|
49
51
|
|
|
52
|
+
|
|
53
|
+
@app.callback(invoke_without_command=True)
|
|
54
|
+
def version_callback(
|
|
55
|
+
ver: bool = typer.Option(
|
|
56
|
+
None,
|
|
57
|
+
"-V",
|
|
58
|
+
"--version",
|
|
59
|
+
is_eager=True,
|
|
60
|
+
help="Show the version and exit.",
|
|
61
|
+
),
|
|
62
|
+
) -> None:
|
|
63
|
+
"""Print version."""
|
|
64
|
+
if ver:
|
|
65
|
+
typer.secho(f"Flower version: {package_version}", fg="blue")
|
|
66
|
+
raise typer.Exit()
|
|
67
|
+
|
|
68
|
+
|
|
50
69
|
if __name__ == "__main__":
|
|
51
70
|
app()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -16,10 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
import hashlib
|
|
19
|
-
import os
|
|
20
|
-
import shutil
|
|
21
|
-
import tempfile
|
|
22
19
|
import zipfile
|
|
20
|
+
from io import BytesIO
|
|
23
21
|
from pathlib import Path
|
|
24
22
|
from typing import Annotated, Any, Optional, Union
|
|
25
23
|
|
|
@@ -29,6 +27,7 @@ import typer
|
|
|
29
27
|
|
|
30
28
|
from flwr.common.constant import FAB_ALLOWED_EXTENSIONS, FAB_DATE, FAB_HASH_TRUNCATION
|
|
31
29
|
|
|
30
|
+
from .config_utils import load as load_toml
|
|
32
31
|
from .config_utils import load_and_validate
|
|
33
32
|
from .utils import is_valid_project_name
|
|
34
33
|
|
|
@@ -43,11 +42,11 @@ def write_to_zip(
|
|
|
43
42
|
return zipfile_obj
|
|
44
43
|
|
|
45
44
|
|
|
46
|
-
def get_fab_filename(
|
|
45
|
+
def get_fab_filename(config: dict[str, Any], fab_hash: str) -> str:
|
|
47
46
|
"""Get the FAB filename based on the given config and FAB hash."""
|
|
48
|
-
publisher =
|
|
49
|
-
name =
|
|
50
|
-
version =
|
|
47
|
+
publisher = config["tool"]["flwr"]["app"]["publisher"]
|
|
48
|
+
name = config["project"]["name"]
|
|
49
|
+
version = config["project"]["version"].replace(".", "-")
|
|
51
50
|
fab_hash_truncated = fab_hash[:FAB_HASH_TRUNCATION]
|
|
52
51
|
return f"{publisher}.{name}.{version}.{fab_hash_truncated}.fab"
|
|
53
52
|
|
|
@@ -89,8 +88,8 @@ def build(
|
|
|
89
88
|
)
|
|
90
89
|
raise typer.Exit(code=1)
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
if
|
|
91
|
+
config, errors, warnings = load_and_validate(app / "pyproject.toml")
|
|
92
|
+
if config is None:
|
|
94
93
|
typer.secho(
|
|
95
94
|
"Project configuration could not be loaded.\npyproject.toml is invalid:\n"
|
|
96
95
|
+ "\n".join([f"- {line}" for line in errors]),
|
|
@@ -107,70 +106,96 @@ def build(
|
|
|
107
106
|
bold=True,
|
|
108
107
|
)
|
|
109
108
|
|
|
110
|
-
#
|
|
111
|
-
|
|
109
|
+
# Build FAB
|
|
110
|
+
fab_bytes, fab_hash, _ = build_fab(app)
|
|
112
111
|
|
|
113
|
-
|
|
112
|
+
# Get the name of the zip file
|
|
113
|
+
fab_filename = get_fab_filename(config, fab_hash)
|
|
114
|
+
|
|
115
|
+
# Write the FAB
|
|
116
|
+
Path(fab_filename).write_bytes(fab_bytes)
|
|
117
|
+
|
|
118
|
+
typer.secho(
|
|
119
|
+
f"🎊 Successfully built {fab_filename}", fg=typer.colors.GREEN, bold=True
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return fab_filename, fab_hash
|
|
114
123
|
|
|
115
|
-
# Remove the 'federations' field from 'tool.flwr' if it exists
|
|
116
|
-
if (
|
|
117
|
-
"tool" in conf
|
|
118
|
-
and "flwr" in conf["tool"]
|
|
119
|
-
and "federations" in conf["tool"]["flwr"]
|
|
120
|
-
):
|
|
121
|
-
del conf["tool"]["flwr"]["federations"]
|
|
122
124
|
|
|
123
|
-
|
|
125
|
+
def build_fab(app: Path) -> tuple[bytes, str, dict[str, Any]]:
|
|
126
|
+
"""Build a FAB in memory and return the bytes, hash, and config.
|
|
124
127
|
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
This function assumes that the provided path points to a valid Flower app and
|
|
129
|
+
bundles it into a FAB without performing additional validation.
|
|
127
130
|
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
app : Path
|
|
134
|
+
Path to the Flower app to bundle into a FAB.
|
|
130
135
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
tuple[bytes, str, dict[str, Any]]
|
|
139
|
+
A tuple containing:
|
|
140
|
+
- the FAB as bytes
|
|
141
|
+
- the SHA256 hash of the FAB
|
|
142
|
+
- the project configuration (with the 'federations' field removed)
|
|
143
|
+
"""
|
|
144
|
+
app = app.resolve()
|
|
145
|
+
|
|
146
|
+
# Load the pyproject.toml file
|
|
147
|
+
config = load_toml(app / "pyproject.toml")
|
|
148
|
+
if config is None:
|
|
149
|
+
raise ValueError("Project configuration could not be loaded.")
|
|
140
150
|
|
|
141
|
-
|
|
151
|
+
# Remove the 'federations' field if it exists
|
|
152
|
+
if (
|
|
153
|
+
"tool" in config
|
|
154
|
+
and "flwr" in config["tool"]
|
|
155
|
+
and "federations" in config["tool"]["flwr"]
|
|
156
|
+
):
|
|
157
|
+
del config["tool"]["flwr"]["federations"]
|
|
142
158
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
with open(file_path, "rb") as f:
|
|
146
|
-
file_contents = f.read()
|
|
159
|
+
# Load .gitignore rules if present
|
|
160
|
+
ignore_spec = _load_gitignore(app)
|
|
147
161
|
|
|
148
|
-
|
|
149
|
-
|
|
162
|
+
# Search for all files in the app directory
|
|
163
|
+
all_files = [
|
|
164
|
+
f
|
|
165
|
+
for f in app.rglob("*")
|
|
166
|
+
if not ignore_spec.match_file(f)
|
|
167
|
+
and f.suffix in FAB_ALLOWED_EXTENSIONS
|
|
168
|
+
and f.name != "pyproject.toml" # Exclude the original pyproject.toml
|
|
169
|
+
]
|
|
170
|
+
all_files.sort()
|
|
171
|
+
|
|
172
|
+
# Create a zip file in memory
|
|
173
|
+
list_file_content = ""
|
|
150
174
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
175
|
+
fab_buffer = BytesIO()
|
|
176
|
+
with zipfile.ZipFile(fab_buffer, "w", zipfile.ZIP_DEFLATED) as fab_file:
|
|
177
|
+
# Add pyproject.toml
|
|
178
|
+
write_to_zip(fab_file, "pyproject.toml", tomli_w.dumps(config))
|
|
155
179
|
|
|
156
|
-
|
|
157
|
-
|
|
180
|
+
for file_path in all_files:
|
|
181
|
+
# Read the file content manually
|
|
182
|
+
file_contents = file_path.read_bytes()
|
|
158
183
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
fab_hash = hashlib.sha256(content).hexdigest()
|
|
184
|
+
archive_path = str(file_path.relative_to(app))
|
|
185
|
+
write_to_zip(fab_file, archive_path, file_contents)
|
|
162
186
|
|
|
163
|
-
|
|
164
|
-
|
|
187
|
+
# Calculate file info
|
|
188
|
+
sha256_hash = hashlib.sha256(file_contents).hexdigest()
|
|
189
|
+
file_size_bits = len(file_contents) * 8 # size in bits
|
|
190
|
+
list_file_content += f"{archive_path},{sha256_hash},{file_size_bits}\n"
|
|
165
191
|
|
|
166
|
-
|
|
167
|
-
|
|
192
|
+
# Add CONTENT and CONTENT.jwt to the zip file
|
|
193
|
+
write_to_zip(fab_file, ".info/CONTENT", list_file_content)
|
|
168
194
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
)
|
|
195
|
+
fab_bytes = fab_buffer.getvalue()
|
|
196
|
+
fab_hash = hashlib.sha256(fab_bytes).hexdigest()
|
|
172
197
|
|
|
173
|
-
return
|
|
198
|
+
return fab_bytes, fab_hash, config
|
|
174
199
|
|
|
175
200
|
|
|
176
201
|
def _load_gitignore(app: Path) -> pathspec.PathSpec:
|