flwr 1.18.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.18.0 → flwr-1.19.0}/PKG-INFO +5 -4
- 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.18.0 → flwr-1.19.0}/py/flwr/cli/build.py +82 -57
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/log.py +3 -3
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/login/login.py +3 -7
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/ls.py +15 -36
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/model.baseline.py.tpl +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.baseline.py.tpl +2 -3
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -17
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/run/run.py +10 -18
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/stop.py +2 -2
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/utils.py +31 -5
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/__init__.py +2 -2
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/client_app.py +1 -1
- flwr-1.19.0/py/flwr/client/clientapp/__init__.py +15 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/grpc_adapter_client/connection.py +4 -4
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/grpc_rere_client/connection.py +130 -60
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/grpc_rere_client/grpc_adapter.py +34 -6
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/message_handler/message_handler.py +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/mod/comms_mods.py +36 -17
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/rest_client/connection.py +173 -67
- flwr-1.19.0/py/flwr/clientapp/__init__.py +15 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/__init__.py +2 -2
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/auth_plugin/__init__.py +2 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/auth_plugin/auth_plugin.py +29 -3
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/constant.py +36 -7
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/event_log_plugin/event_log_plugin.py +3 -3
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/exit_handlers.py +30 -0
- 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.18.0 → flwr-1.19.0}/py/flwr/common/message.py +110 -242
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/record/__init__.py +2 -1
- flwr-1.19.0/py/flwr/common/record/array.py +323 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/record/arrayrecord.py +103 -225
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/record/configrecord.py +59 -4
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/record/conversion_utils.py +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/record/metricrecord.py +55 -4
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/record/recorddict.py +69 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/recorddict_compat.py +2 -2
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/retry_invoker.py +5 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/serde.py +59 -183
- flwr-1.19.0/py/flwr/common/serde_utils.py +175 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/typing.py +5 -3
- 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.18.0/py/flwr → flwr-1.19.0/py/flwr/compat}/client/app.py +19 -159
- 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.19.0/py/flwr/proto/fleet_pb2.py +61 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/fleet_pb2.pyi +49 -35
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/fleet_pb2_grpc.py +117 -13
- {flwr-1.18.0 → 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.18.0 → flwr-1.19.0}/py/flwr/proto/message_pb2.pyi +125 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/recorddict_pb2.py +16 -28
- {flwr-1.18.0 → 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.18.0 → 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.18.0 → flwr-1.19.0}/py/flwr/proto/serverappio_pb2.pyi +45 -3
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/serverappio_pb2_grpc.py +138 -34
- {flwr-1.18.0 → 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.18.0 → flwr-1.19.0}/py/flwr/proto/simulationio_pb2_grpc.py +35 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/simulationio_pb2_grpc.pyi +14 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/__init__.py +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/app.py +68 -186
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/compat/app_utils.py +50 -28
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/fleet_event_log_interceptor.py +2 -2
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/grid/grpc_grid.py +104 -34
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/grid/inmemory_grid.py +5 -4
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/serverapp/app.py +18 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/ffs/__init__.py +2 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +13 -3
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +101 -7
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/message_handler/message_handler.py +135 -18
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/rest_rere/rest_api.py +72 -11
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/vce/vce_api.py +6 -3
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/linkstate/in_memory_linkstate.py +138 -43
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/linkstate/linkstate.py +53 -20
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/linkstate/sqlite_linkstate.py +149 -55
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/linkstate/utils.py +33 -29
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/serverappio/serverappio_grpc.py +3 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/serverappio/serverappio_servicer.py +211 -57
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/simulation/simulationio_servicer.py +25 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/utils.py +44 -2
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/utils/validator.py +2 -2
- flwr-1.19.0/py/flwr/serverapp/__init__.py +15 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/simulation/app.py +17 -0
- 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.18.0 → flwr-1.19.0}/py/flwr/superexec/deployment.py +6 -2
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/superexec/exec_event_log_interceptor.py +4 -4
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/superexec/exec_grpc.py +7 -3
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/superexec/exec_servicer.py +125 -23
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/superexec/exec_user_auth_interceptor.py +37 -8
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/superexec/executor.py +4 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/superexec/simulation.py +7 -1
- flwr-1.19.0/py/flwr/superlink/__init__.py +15 -0
- flwr-1.19.0/py/flwr/supernode/__init__.py +15 -0
- {flwr-1.18.0/py/flwr/client/clientapp → flwr-1.19.0/py/flwr/supernode/cli}/__init__.py +4 -2
- flwr-1.18.0/py/flwr/client/supernode/app.py → flwr-1.19.0/py/flwr/supernode/cli/flower_supernode.py +3 -12
- flwr-1.19.0/py/flwr/supernode/cli/flwr_clientapp.py +81 -0
- 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.19.0/py/flwr/supernode/runtime/__init__.py +15 -0
- flwr-1.18.0/py/flwr/client/clientapp/app.py → flwr-1.19.0/py/flwr/supernode/runtime/run_clientapp.py +25 -56
- 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.19.0/py/flwr/supernode/start_client_internal.py +491 -0
- {flwr-1.18.0 → flwr-1.19.0}/pyproject.toml +5 -4
- flwr-1.18.0/py/flwr/client/heartbeat.py +0 -74
- flwr-1.18.0/py/flwr/client/nodestate/in_memory_nodestate.py +0 -38
- flwr-1.18.0/py/flwr/client/nodestate/nodestate.py +0 -31
- flwr-1.18.0/py/flwr/client/supernode/__init__.py +0 -22
- flwr-1.18.0/py/flwr/proto/fleet_pb2.py +0 -56
- flwr-1.18.0/py/flwr/proto/message_pb2.py +0 -41
- flwr-1.18.0/py/flwr/proto/run_pb2.py +0 -64
- flwr-1.18.0/py/flwr/proto/serverappio_pb2.py +0 -51
- flwr-1.18.0/py/flwr/proto/simulationio_pb2.py +0 -38
- {flwr-1.18.0 → flwr-1.19.0}/README.md +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/app.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/auth_plugin/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/auth_plugin/oidc_cli_plugin.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/cli_user_auth_interceptor.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/config_utils.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/constant.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/example.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/install.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/login/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/new.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/.gitignore.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/LICENSE.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/README.baseline.md.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/README.md.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/__init__.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.baseline.py.tpl +1 -1
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/cli/run/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/client.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/clientapp/utils.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/dpfedavg_numpy_client.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/grpc_adapter_client/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/grpc_rere_client/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/grpc_rere_client/client_interceptor.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/message_handler/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/mod/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/mod/centraldp_mods.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/mod/localdp_mod.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/mod/secure_aggregation/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/mod/secure_aggregation/secagg_mod.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/mod/secure_aggregation/secaggplus_mod.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/mod/utils.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/numpy_client.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/rest_client/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/run_info_store.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/client/typing.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/address.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/args.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/config.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/context.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/date.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/differential_privacy.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/differential_privacy_constants.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/dp.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/event_log_plugin/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/exit/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/exit/exit.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/exit/exit_code.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/grpc.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/logger.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/object_ref.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/parameter.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/pyproject.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/record/typeddict.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/secure_aggregation/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/secure_aggregation/crypto/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/secure_aggregation/crypto/shamir.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/secure_aggregation/crypto/symmetric_encryption.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/secure_aggregation/ndarrays_arithmetic.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/secure_aggregation/quantization.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/secure_aggregation/secaggplus_constants.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/secure_aggregation/secaggplus_utils.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/telemetry.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/common/version.py +0 -0
- {flwr-1.18.0/py/flwr → flwr-1.19.0/py/flwr/compat}/client/grpc_client/__init__.py +0 -0
- {flwr-1.18.0/py/flwr → flwr-1.19.0/py/flwr/compat}/client/grpc_client/connection.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/clientappio_pb2.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/clientappio_pb2.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/clientappio_pb2_grpc.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/clientappio_pb2_grpc.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/error_pb2.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/error_pb2.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/error_pb2_grpc.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/error_pb2_grpc.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/exec_pb2.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/exec_pb2.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/exec_pb2_grpc.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/exec_pb2_grpc.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/fab_pb2.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/fab_pb2.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/fab_pb2_grpc.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/fab_pb2_grpc.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/grpcadapter_pb2.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/grpcadapter_pb2.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/grpcadapter_pb2_grpc.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/grpcadapter_pb2_grpc.pyi +0 -0
- /flwr-1.18.0/py/flwr/proto/log_pb2_grpc.py → /flwr-1.19.0/py/flwr/proto/heartbeat_pb2_grpc.py +0 -0
- /flwr-1.18.0/py/flwr/proto/log_pb2_grpc.pyi → /flwr-1.19.0/py/flwr/proto/heartbeat_pb2_grpc.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/log_pb2.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/log_pb2.pyi +0 -0
- /flwr-1.18.0/py/flwr/proto/message_pb2_grpc.py → /flwr-1.19.0/py/flwr/proto/log_pb2_grpc.py +0 -0
- /flwr-1.18.0/py/flwr/proto/message_pb2_grpc.pyi → /flwr-1.19.0/py/flwr/proto/log_pb2_grpc.pyi +0 -0
- /flwr-1.18.0/py/flwr/proto/node_pb2_grpc.py → /flwr-1.19.0/py/flwr/proto/message_pb2_grpc.py +0 -0
- /flwr-1.18.0/py/flwr/proto/node_pb2_grpc.pyi → /flwr-1.19.0/py/flwr/proto/message_pb2_grpc.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/node_pb2.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/node_pb2.pyi +0 -0
- /flwr-1.18.0/py/flwr/proto/recorddict_pb2_grpc.py → /flwr-1.19.0/py/flwr/proto/node_pb2_grpc.py +0 -0
- /flwr-1.18.0/py/flwr/proto/recorddict_pb2_grpc.pyi → /flwr-1.19.0/py/flwr/proto/node_pb2_grpc.pyi +0 -0
- /flwr-1.18.0/py/flwr/proto/run_pb2_grpc.py → /flwr-1.19.0/py/flwr/proto/recorddict_pb2_grpc.py +0 -0
- /flwr-1.18.0/py/flwr/proto/run_pb2_grpc.pyi → /flwr-1.19.0/py/flwr/proto/recorddict_pb2_grpc.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/simulationio_pb2.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/transport_pb2.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/transport_pb2.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/transport_pb2_grpc.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/proto/transport_pb2_grpc.pyi +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/py.typed +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/client_manager.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/client_proxy.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/compat/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/compat/app.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/compat/grid_client_proxy.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/compat/legacy_context.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/criterion.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/grid/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/grid/grid.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/history.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/run_serverapp.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/server.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/server_app.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/server_config.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/serverapp/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/serverapp_components.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/aggregate.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/bulyan.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/dp_adaptive_clipping.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/dp_fixed_clipping.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/dpfedavg_adaptive.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/dpfedavg_fixed.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fault_tolerant_fedavg.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedadagrad.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedadam.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedavg.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedavg_android.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedavgm.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedmedian.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedopt.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedprox.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedtrimmedavg.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedxgb_bagging.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedxgb_cyclic.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedxgb_nn_avg.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/fedyogi.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/krum.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/qfedavg.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/strategy/strategy.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/ffs/disk_ffs.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/ffs/ffs.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/ffs/ffs_factory.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_adapter/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_bidi/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_rere/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/message_handler/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/rest_rere/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/vce/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/vce/backend/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/vce/backend/backend.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/fleet/vce/backend/raybackend.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/linkstate/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/linkstate/linkstate_factory.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/serverappio/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/simulation/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/superlink/simulation/simulationio_grpc.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/typing.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/utils/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/utils/tensorboard.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/workflow/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/workflow/constant.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/workflow/default_workflows.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/workflow/secure_aggregation/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/workflow/secure_aggregation/secagg_workflow.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/simulation/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/simulation/legacy_app.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/simulation/ray_transport/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/simulation/ray_transport/ray_actor.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/simulation/ray_transport/ray_client_proxy.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/simulation/ray_transport/utils.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/simulation/run_simulation.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/simulation/simulationio_connection.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/superexec/__init__.py +0 -0
- {flwr-1.18.0 → flwr-1.19.0}/py/flwr/superexec/app.py +0 -0
- {flwr-1.18.0/py/flwr/client → flwr-1.19.0/py/flwr/supernode}/nodestate/__init__.py +0 -0
- {flwr-1.18.0/py/flwr/client → flwr-1.19.0/py/flwr/supernode}/nodestate/nodestate_factory.py +0 -0
- {flwr-1.18.0/py/flwr/client/clientapp → flwr-1.19.0/py/flwr/supernode/servicer/clientappio}/clientappio_servicer.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
|
|
|
@@ -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
|
|
@@ -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:
|
|
@@ -35,7 +35,7 @@ from flwr.common.logger import log as logger
|
|
|
35
35
|
from flwr.proto.exec_pb2 import StreamLogsRequest # pylint: disable=E0611
|
|
36
36
|
from flwr.proto.exec_pb2_grpc import ExecStub
|
|
37
37
|
|
|
38
|
-
from .utils import init_channel, try_obtain_cli_auth_plugin
|
|
38
|
+
from .utils import flwr_cli_grpc_exc_handler, init_channel, try_obtain_cli_auth_plugin
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
class AllLogsRetrieved(BaseException):
|
|
@@ -95,7 +95,7 @@ def stream_logs(
|
|
|
95
95
|
latest_timestamp = 0.0
|
|
96
96
|
res = None
|
|
97
97
|
try:
|
|
98
|
-
with
|
|
98
|
+
with flwr_cli_grpc_exc_handler():
|
|
99
99
|
for res in stub.StreamLogs(req, timeout=duration):
|
|
100
100
|
print(res.log_output, end="")
|
|
101
101
|
raise AllLogsRetrieved()
|
|
@@ -116,7 +116,7 @@ def print_logs(run_id: int, channel: grpc.Channel, timeout: int) -> None:
|
|
|
116
116
|
req = StreamLogsRequest(run_id=run_id, after_timestamp=0.0)
|
|
117
117
|
|
|
118
118
|
try:
|
|
119
|
-
with
|
|
119
|
+
with flwr_cli_grpc_exc_handler():
|
|
120
120
|
# Enforce timeout for graceful exit
|
|
121
121
|
for res in stub.StreamLogs(req, timeout=timeout):
|
|
122
122
|
print(res.log_output)
|
|
@@ -35,11 +35,7 @@ from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
|
35
35
|
)
|
|
36
36
|
from flwr.proto.exec_pb2_grpc import ExecStub
|
|
37
37
|
|
|
38
|
-
from ..utils import
|
|
39
|
-
init_channel,
|
|
40
|
-
try_obtain_cli_auth_plugin,
|
|
41
|
-
unauthenticated_exc_handler,
|
|
42
|
-
)
|
|
38
|
+
from ..utils import flwr_cli_grpc_exc_handler, init_channel, try_obtain_cli_auth_plugin
|
|
43
39
|
|
|
44
40
|
|
|
45
41
|
def login( # pylint: disable=R0914
|
|
@@ -96,7 +92,7 @@ def login( # pylint: disable=R0914
|
|
|
96
92
|
stub = ExecStub(channel)
|
|
97
93
|
|
|
98
94
|
login_request = GetLoginDetailsRequest()
|
|
99
|
-
with
|
|
95
|
+
with flwr_cli_grpc_exc_handler():
|
|
100
96
|
login_response: GetLoginDetailsResponse = stub.GetLoginDetails(login_request)
|
|
101
97
|
|
|
102
98
|
# Get the auth plugin
|
|
@@ -120,7 +116,7 @@ def login( # pylint: disable=R0914
|
|
|
120
116
|
expires_in=login_response.expires_in,
|
|
121
117
|
interval=login_response.interval,
|
|
122
118
|
)
|
|
123
|
-
with
|
|
119
|
+
with flwr_cli_grpc_exc_handler():
|
|
124
120
|
credentials = auth_plugin.login(details, stub)
|
|
125
121
|
|
|
126
122
|
# Store the tokens
|
|
@@ -44,7 +44,7 @@ from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
|
44
44
|
)
|
|
45
45
|
from flwr.proto.exec_pb2_grpc import ExecStub
|
|
46
46
|
|
|
47
|
-
from .utils import init_channel, try_obtain_cli_auth_plugin
|
|
47
|
+
from .utils import flwr_cli_grpc_exc_handler, init_channel, try_obtain_cli_auth_plugin
|
|
48
48
|
|
|
49
49
|
_RunListType = tuple[int, str, str, str, str, str, str, str, str]
|
|
50
50
|
|
|
@@ -130,23 +130,16 @@ def ls( # pylint: disable=too-many-locals, too-many-branches, R0913, R0917
|
|
|
130
130
|
# Display information about a specific run ID
|
|
131
131
|
if run_id is not None:
|
|
132
132
|
typer.echo(f"🔍 Displaying information for run ID {run_id}...")
|
|
133
|
-
|
|
134
|
-
_display_one_run(stub, run_id, output_format)
|
|
133
|
+
formatted_runs = _display_one_run(stub, run_id)
|
|
135
134
|
# By default, list all runs
|
|
136
135
|
else:
|
|
137
136
|
typer.echo("📄 Listing all runs...")
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
typer.secho(
|
|
145
|
-
f"❌ {err}",
|
|
146
|
-
fg=typer.colors.RED,
|
|
147
|
-
bold=True,
|
|
148
|
-
)
|
|
149
|
-
raise typer.Exit(code=1) from err
|
|
137
|
+
formatted_runs = _list_runs(stub)
|
|
138
|
+
restore_output()
|
|
139
|
+
if output_format == CliOutputFormat.JSON:
|
|
140
|
+
Console().print_json(_to_json(formatted_runs))
|
|
141
|
+
else:
|
|
142
|
+
Console().print(_to_table(formatted_runs))
|
|
150
143
|
finally:
|
|
151
144
|
if channel:
|
|
152
145
|
channel.close()
|
|
@@ -300,37 +293,23 @@ def _to_json(run_list: list[_RunListType]) -> str:
|
|
|
300
293
|
return json.dumps({"success": True, "runs": runs_list})
|
|
301
294
|
|
|
302
295
|
|
|
303
|
-
def _list_runs(
|
|
304
|
-
stub: ExecStub,
|
|
305
|
-
output_format: str = CliOutputFormat.DEFAULT,
|
|
306
|
-
) -> None:
|
|
296
|
+
def _list_runs(stub: ExecStub) -> list[_RunListType]:
|
|
307
297
|
"""List all runs."""
|
|
308
|
-
with
|
|
298
|
+
with flwr_cli_grpc_exc_handler():
|
|
309
299
|
res: ListRunsResponse = stub.ListRuns(ListRunsRequest())
|
|
310
300
|
run_dict = {run_id: run_from_proto(proto) for run_id, proto in res.run_dict.items()}
|
|
311
301
|
|
|
312
|
-
|
|
313
|
-
if output_format == CliOutputFormat.JSON:
|
|
314
|
-
Console().print_json(_to_json(formatted_runs))
|
|
315
|
-
else:
|
|
316
|
-
Console().print(_to_table(formatted_runs))
|
|
302
|
+
return _format_runs(run_dict, res.now)
|
|
317
303
|
|
|
318
304
|
|
|
319
|
-
def _display_one_run(
|
|
320
|
-
stub: ExecStub,
|
|
321
|
-
run_id: int,
|
|
322
|
-
output_format: str = CliOutputFormat.DEFAULT,
|
|
323
|
-
) -> None:
|
|
305
|
+
def _display_one_run(stub: ExecStub, run_id: int) -> list[_RunListType]:
|
|
324
306
|
"""Display information about a specific run."""
|
|
325
|
-
with
|
|
307
|
+
with flwr_cli_grpc_exc_handler():
|
|
326
308
|
res: ListRunsResponse = stub.ListRuns(ListRunsRequest(run_id=run_id))
|
|
327
309
|
if not res.run_dict:
|
|
310
|
+
# This won't be reached as an gRPC error is raised if run_id is invalid
|
|
328
311
|
raise ValueError(f"Run ID {run_id} not found")
|
|
329
312
|
|
|
330
313
|
run_dict = {run_id: run_from_proto(proto) for run_id, proto in res.run_dict.items()}
|
|
331
314
|
|
|
332
|
-
|
|
333
|
-
if output_format == CliOutputFormat.JSON:
|
|
334
|
-
Console().print_json(_to_json(formatted_runs))
|
|
335
|
-
else:
|
|
336
|
-
Console().print(_to_table(formatted_runs))
|
|
315
|
+
return _format_runs(run_dict, res.now)
|