flwr-nightly 1.8.0.dev20240328__tar.gz → 1.9.0.dev20240404__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.

Potentially problematic release.


This version of flwr-nightly might be problematic. Click here for more details.

Files changed (213) hide show
  1. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/PKG-INFO +1 -1
  2. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/pyproject.toml +1 -1
  3. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/code/task.pytorch.py.tpl +1 -1
  4. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
  5. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +1 -1
  6. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/app.py +26 -13
  7. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/client_app.py +16 -0
  8. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/grpc_rere_client/connection.py +71 -29
  9. flwr_nightly-1.9.0.dev20240404/src/py/flwr/client/heartbeat.py +72 -0
  10. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/rest_client/connection.py +102 -28
  11. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/constant.py +20 -0
  12. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/logger.py +4 -4
  13. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/message.py +15 -0
  14. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/retry_invoker.py +24 -13
  15. flwr_nightly-1.9.0.dev20240404/src/py/flwr/proto/fleet_pb2.py +54 -0
  16. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/fleet_pb2.pyi +5 -0
  17. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/driver/driver.py +15 -5
  18. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/server_app.py +3 -0
  19. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/message_handler/message_handler.py +3 -2
  20. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/rest_rere/rest_api.py +28 -0
  21. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/vce/vce_api.py +22 -4
  22. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/state/in_memory_state.py +25 -8
  23. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/state/sqlite_state.py +53 -5
  24. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/state/state.py +1 -1
  25. flwr_nightly-1.9.0.dev20240404/src/py/flwr/server/superlink/state/utils.py +56 -0
  26. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/workflow/default_workflows.py +1 -4
  27. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +0 -5
  28. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/simulation/ray_transport/ray_actor.py +2 -22
  29. flwr_nightly-1.8.0.dev20240328/src/py/flwr/proto/fleet_pb2.py +0 -54
  30. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/LICENSE +0 -0
  31. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/README.md +0 -0
  32. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/__init__.py +0 -0
  33. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/__init__.py +0 -0
  34. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/app.py +0 -0
  35. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/example.py +0 -0
  36. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/flower_toml.py +0 -0
  37. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/__init__.py +0 -0
  38. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/new.py +0 -0
  39. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/__init__.py +0 -0
  40. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/README.md.tpl +0 -0
  41. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/__init__.py +0 -0
  42. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/code/__init__.py +0 -0
  43. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/code/__init__.py.tpl +0 -0
  44. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -0
  45. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -0
  46. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -0
  47. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -0
  48. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -0
  49. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -0
  50. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/flower.toml.tpl +0 -0
  51. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -0
  52. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -0
  53. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -0
  54. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -0
  55. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/run/__init__.py +0 -0
  56. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/run/run.py +0 -0
  57. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/cli/utils.py +0 -0
  58. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/__init__.py +0 -0
  59. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/client.py +0 -0
  60. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/dpfedavg_numpy_client.py +0 -0
  61. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/grpc_client/__init__.py +0 -0
  62. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/grpc_client/connection.py +0 -0
  63. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/grpc_rere_client/__init__.py +0 -0
  64. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/message_handler/__init__.py +0 -0
  65. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/message_handler/message_handler.py +0 -0
  66. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/message_handler/task_handler.py +0 -0
  67. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/mod/__init__.py +0 -0
  68. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/mod/centraldp_mods.py +0 -0
  69. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/mod/comms_mods.py +0 -0
  70. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/mod/localdp_mod.py +0 -0
  71. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/mod/secure_aggregation/__init__.py +0 -0
  72. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/mod/secure_aggregation/secagg_mod.py +0 -0
  73. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/mod/secure_aggregation/secaggplus_mod.py +0 -0
  74. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/mod/utils.py +0 -0
  75. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/node_state.py +0 -0
  76. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/node_state_tests.py +0 -0
  77. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/numpy_client.py +0 -0
  78. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/rest_client/__init__.py +0 -0
  79. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/client/typing.py +0 -0
  80. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/__init__.py +0 -0
  81. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/address.py +0 -0
  82. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/context.py +0 -0
  83. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/date.py +0 -0
  84. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/differential_privacy.py +0 -0
  85. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/differential_privacy_constants.py +0 -0
  86. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/dp.py +0 -0
  87. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/exit_handlers.py +0 -0
  88. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/grpc.py +0 -0
  89. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/object_ref.py +0 -0
  90. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/parameter.py +0 -0
  91. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/pyproject.py +0 -0
  92. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/record/__init__.py +0 -0
  93. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/record/configsrecord.py +0 -0
  94. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/record/conversion_utils.py +0 -0
  95. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/record/metricsrecord.py +0 -0
  96. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/record/parametersrecord.py +0 -0
  97. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/record/recordset.py +0 -0
  98. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/record/typeddict.py +0 -0
  99. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/recordset_compat.py +0 -0
  100. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/secure_aggregation/__init__.py +0 -0
  101. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/secure_aggregation/crypto/__init__.py +0 -0
  102. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/secure_aggregation/crypto/shamir.py +0 -0
  103. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/secure_aggregation/crypto/symmetric_encryption.py +0 -0
  104. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/secure_aggregation/ndarrays_arithmetic.py +0 -0
  105. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/secure_aggregation/quantization.py +0 -0
  106. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/secure_aggregation/secaggplus_constants.py +0 -0
  107. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/secure_aggregation/secaggplus_utils.py +0 -0
  108. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/serde.py +0 -0
  109. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/telemetry.py +0 -0
  110. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/typing.py +0 -0
  111. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/common/version.py +0 -0
  112. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/__init__.py +0 -0
  113. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/driver_pb2.py +0 -0
  114. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/driver_pb2.pyi +0 -0
  115. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/driver_pb2_grpc.py +0 -0
  116. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/driver_pb2_grpc.pyi +0 -0
  117. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/error_pb2.py +0 -0
  118. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/error_pb2.pyi +0 -0
  119. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/error_pb2_grpc.py +0 -0
  120. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/error_pb2_grpc.pyi +0 -0
  121. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/fleet_pb2_grpc.py +0 -0
  122. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/fleet_pb2_grpc.pyi +0 -0
  123. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/node_pb2.py +0 -0
  124. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/node_pb2.pyi +0 -0
  125. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/node_pb2_grpc.py +0 -0
  126. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/node_pb2_grpc.pyi +0 -0
  127. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/recordset_pb2.py +0 -0
  128. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/recordset_pb2.pyi +0 -0
  129. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/recordset_pb2_grpc.py +0 -0
  130. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/recordset_pb2_grpc.pyi +0 -0
  131. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/task_pb2.py +0 -0
  132. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/task_pb2.pyi +0 -0
  133. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/task_pb2_grpc.py +0 -0
  134. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/task_pb2_grpc.pyi +0 -0
  135. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/transport_pb2.py +0 -0
  136. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/transport_pb2.pyi +0 -0
  137. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/transport_pb2_grpc.py +0 -0
  138. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/proto/transport_pb2_grpc.pyi +0 -0
  139. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/py.typed +0 -0
  140. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/__init__.py +0 -0
  141. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/app.py +0 -0
  142. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/client_manager.py +0 -0
  143. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/client_proxy.py +0 -0
  144. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/compat/__init__.py +0 -0
  145. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/compat/app.py +0 -0
  146. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/compat/app_utils.py +0 -0
  147. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/compat/driver_client_proxy.py +0 -0
  148. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/compat/legacy_context.py +0 -0
  149. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/criterion.py +0 -0
  150. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/driver/__init__.py +0 -0
  151. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/driver/grpc_driver.py +0 -0
  152. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/history.py +0 -0
  153. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/run_serverapp.py +0 -0
  154. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/server.py +0 -0
  155. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/server_config.py +0 -0
  156. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/__init__.py +0 -0
  157. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/aggregate.py +0 -0
  158. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/bulyan.py +0 -0
  159. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/dp_adaptive_clipping.py +0 -0
  160. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/dp_fixed_clipping.py +0 -0
  161. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/dpfedavg_adaptive.py +0 -0
  162. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/dpfedavg_fixed.py +0 -0
  163. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fault_tolerant_fedavg.py +0 -0
  164. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedadagrad.py +0 -0
  165. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedadam.py +0 -0
  166. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedavg.py +0 -0
  167. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedavg_android.py +0 -0
  168. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedavgm.py +0 -0
  169. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedmedian.py +0 -0
  170. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedopt.py +0 -0
  171. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedprox.py +0 -0
  172. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedtrimmedavg.py +0 -0
  173. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedxgb_bagging.py +0 -0
  174. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedxgb_cyclic.py +0 -0
  175. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedxgb_nn_avg.py +0 -0
  176. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/fedyogi.py +0 -0
  177. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/krum.py +0 -0
  178. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/qfedavg.py +0 -0
  179. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/strategy/strategy.py +0 -0
  180. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/__init__.py +0 -0
  181. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/driver/__init__.py +0 -0
  182. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/driver/driver_grpc.py +0 -0
  183. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/driver/driver_servicer.py +0 -0
  184. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/__init__.py +0 -0
  185. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/grpc_bidi/__init__.py +0 -0
  186. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +0 -0
  187. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +0 -0
  188. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +0 -0
  189. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +0 -0
  190. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/grpc_rere/__init__.py +0 -0
  191. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +0 -0
  192. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/message_handler/__init__.py +0 -0
  193. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/rest_rere/__init__.py +0 -0
  194. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/vce/__init__.py +0 -0
  195. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/vce/backend/__init__.py +0 -0
  196. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/vce/backend/backend.py +0 -0
  197. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/fleet/vce/backend/raybackend.py +0 -0
  198. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/state/__init__.py +0 -0
  199. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/superlink/state/state_factory.py +0 -0
  200. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/typing.py +0 -0
  201. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/utils/__init__.py +0 -0
  202. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/utils/tensorboard.py +0 -0
  203. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/utils/validator.py +0 -0
  204. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/workflow/__init__.py +0 -0
  205. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/workflow/constant.py +0 -0
  206. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/workflow/secure_aggregation/__init__.py +0 -0
  207. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/server/workflow/secure_aggregation/secagg_workflow.py +0 -0
  208. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/simulation/__init__.py +0 -0
  209. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/simulation/app.py +0 -0
  210. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/simulation/ray_transport/__init__.py +0 -0
  211. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/simulation/ray_transport/ray_client_proxy.py +0 -0
  212. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/simulation/ray_transport/utils.py +0 -0
  213. {flwr_nightly-1.8.0.dev20240328 → flwr_nightly-1.9.0.dev20240404}/src/py/flwr/simulation/run_simulation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.8.0.dev20240328
3
+ Version: 1.9.0.dev20240404
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "flwr-nightly"
7
- version = "1.8.0-dev20240328"
7
+ version = "1.9.0-dev20240404"
8
8
  description = "Flower: A Friendly Federated Learning Framework"
9
9
  license = "Apache-2.0"
10
10
  authors = ["The Flower Authors <hello@flower.ai>"]
@@ -39,7 +39,7 @@ def load_data(partition_id, num_partitions):
39
39
  fds = FederatedDataset(dataset="cifar10", partitioners={"train": num_partitions})
40
40
  partition = fds.load_partition(partition_id)
41
41
  # Divide data on each node: 80% train, 20% test
42
- partition_train_test = partition.train_test_split(test_size=0.2)
42
+ partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
43
43
  pytorch_transforms = Compose(
44
44
  [ToTensor(), Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
45
45
  )
@@ -15,7 +15,7 @@ readme = "README.md"
15
15
  [tool.poetry.dependencies]
16
16
  python = "^3.9"
17
17
  # Mandatory dependencies
18
- flwr-nightly = { version = "1.8.0.dev20240313", extras = ["simulation"] }
18
+ flwr = { version = "^1.8.0", extras = ["simulation"] }
19
19
  flwr-datasets = { version = "0.0.2", extras = ["vision"] }
20
20
  torch = "2.2.1"
21
21
  torchvision = "0.17.1"
@@ -1,4 +1,4 @@
1
- flwr-nightly[simulation]==1.8.0.dev20240313
1
+ flwr[simulation]>=1.8.0
2
2
  flwr-datasets[vision]==0.0.2
3
3
  torch==2.2.1
4
4
  torchvision==0.17.1
@@ -34,9 +34,10 @@ from flwr.common.constant import (
34
34
  TRANSPORT_TYPE_GRPC_RERE,
35
35
  TRANSPORT_TYPE_REST,
36
36
  TRANSPORT_TYPES,
37
+ ErrorCode,
37
38
  )
38
39
  from flwr.common.exit_handlers import register_exit_handlers
39
- from flwr.common.logger import log, warn_deprecated_feature, warn_experimental_feature
40
+ from flwr.common.logger import log, warn_deprecated_feature
40
41
  from flwr.common.message import Error
41
42
  from flwr.common.object_ref import load_app, validate
42
43
  from flwr.common.retry_invoker import RetryInvoker, exponential
@@ -385,8 +386,6 @@ def _start_client_internal(
385
386
  return ClientApp(client_fn=client_fn)
386
387
 
387
388
  load_client_app_fn = _load_client_app
388
- else:
389
- warn_experimental_feature("`load_client_app_fn`")
390
389
 
391
390
  # At this point, only `load_client_app_fn` should be used
392
391
  # Both `client` and `client_fn` must not be used directly
@@ -397,7 +396,7 @@ def _start_client_internal(
397
396
  )
398
397
 
399
398
  retry_invoker = RetryInvoker(
400
- wait_factory=exponential,
399
+ wait_gen_factory=exponential,
401
400
  recoverable_exceptions=connection_error_type,
402
401
  max_tries=max_retries,
403
402
  max_time=max_wait_time,
@@ -485,7 +484,7 @@ def _start_client_internal(
485
484
  # Create an error reply message that will never be used to prevent
486
485
  # the used-before-assignment linting error
487
486
  reply_message = message.create_error_reply(
488
- error=Error(code=0, reason="Unknown")
487
+ error=Error(code=ErrorCode.UNKNOWN, reason="Unknown")
489
488
  )
490
489
 
491
490
  # Handle app loading and task message
@@ -493,27 +492,41 @@ def _start_client_internal(
493
492
  # Load ClientApp instance
494
493
  client_app: ClientApp = load_client_app_fn()
495
494
 
495
+ # Execute ClientApp
496
496
  reply_message = client_app(message=message, context=context)
497
- # Update node state
498
- node_state.update_context(
499
- run_id=message.metadata.run_id,
500
- context=context,
501
- )
502
497
  except Exception as ex: # pylint: disable=broad-exception-caught
503
- log(ERROR, "ClientApp raised an exception", exc_info=ex)
504
498
 
505
499
  # Legacy grpc-bidi
506
500
  if transport in ["grpc-bidi", None]:
501
+ log(ERROR, "Client raised an exception.", exc_info=ex)
507
502
  # Raise exception, crash process
508
503
  raise ex
509
504
 
510
505
  # Don't update/change NodeState
511
506
 
512
- # Create error message
507
+ e_code = ErrorCode.CLIENT_APP_RAISED_EXCEPTION
513
508
  # Reason example: "<class 'ZeroDivisionError'>:<'division by zero'>"
514
509
  reason = str(type(ex)) + ":<'" + str(ex) + "'>"
510
+ exc_entity = "ClientApp"
511
+ if isinstance(ex, LoadClientAppError):
512
+ reason = (
513
+ "An exception was raised when attempting to load "
514
+ "`ClientApp`"
515
+ )
516
+ e_code = ErrorCode.LOAD_CLIENT_APP_EXCEPTION
517
+ exc_entity = "SuperNode"
518
+
519
+ log(ERROR, "%s raised an exception", exc_entity, exc_info=ex)
520
+
521
+ # Create error message
515
522
  reply_message = message.create_error_reply(
516
- error=Error(code=0, reason=reason)
523
+ error=Error(code=e_code, reason=reason)
524
+ )
525
+ else:
526
+ # No exception, update node state
527
+ node_state.update_context(
528
+ run_id=message.metadata.run_id,
529
+ context=context,
517
530
  )
518
531
 
519
532
  # Send
@@ -23,10 +23,20 @@ from flwr.client.message_handler.message_handler import (
23
23
  from flwr.client.mod.utils import make_ffn
24
24
  from flwr.client.typing import ClientFn, Mod
25
25
  from flwr.common import Context, Message, MessageType
26
+ from flwr.common.logger import warn_preview_feature
26
27
 
27
28
  from .typing import ClientAppCallable
28
29
 
29
30
 
31
+ class ClientAppException(Exception):
32
+ """Exception raised when an exception is raised while executing a ClientApp."""
33
+
34
+ def __init__(self, message: str):
35
+ ex_name = self.__class__.__name__
36
+ self.message = f"\nException {ex_name} occurred. Message: " + message
37
+ super().__init__(self.message)
38
+
39
+
30
40
  class ClientApp:
31
41
  """Flower ClientApp.
32
42
 
@@ -123,6 +133,8 @@ class ClientApp:
123
133
  if self._call:
124
134
  raise _registration_error(MessageType.TRAIN)
125
135
 
136
+ warn_preview_feature("ClientApp-register-train-function")
137
+
126
138
  # Register provided function with the ClientApp object
127
139
  # Wrap mods around the wrapped step function
128
140
  self._train = make_ffn(train_fn, self._mods)
@@ -151,6 +163,8 @@ class ClientApp:
151
163
  if self._call:
152
164
  raise _registration_error(MessageType.EVALUATE)
153
165
 
166
+ warn_preview_feature("ClientApp-register-evaluate-function")
167
+
154
168
  # Register provided function with the ClientApp object
155
169
  # Wrap mods around the wrapped step function
156
170
  self._evaluate = make_ffn(evaluate_fn, self._mods)
@@ -179,6 +193,8 @@ class ClientApp:
179
193
  if self._call:
180
194
  raise _registration_error(MessageType.QUERY)
181
195
 
196
+ warn_preview_feature("ClientApp-register-query-function")
197
+
182
198
  # Register provided function with the ClientApp object
183
199
  # Wrap mods around the wrapped step function
184
200
  self._query = make_ffn(query_fn, self._mods)
@@ -15,23 +15,34 @@
15
15
  """Contextmanager for a gRPC request-response channel to the Flower server."""
16
16
 
17
17
 
18
+ import random
19
+ import threading
18
20
  from contextlib import contextmanager
19
21
  from copy import copy
20
22
  from logging import DEBUG, ERROR
21
23
  from pathlib import Path
22
- from typing import Callable, Dict, Iterator, Optional, Tuple, Union, cast
24
+ from typing import Callable, Iterator, Optional, Tuple, Union, cast
23
25
 
26
+ from flwr.client.heartbeat import start_ping_loop
24
27
  from flwr.client.message_handler.message_handler import validate_out_message
25
28
  from flwr.client.message_handler.task_handler import get_task_ins, validate_task_ins
26
29
  from flwr.common import GRPC_MAX_MESSAGE_LENGTH
30
+ from flwr.common.constant import (
31
+ PING_BASE_MULTIPLIER,
32
+ PING_CALL_TIMEOUT,
33
+ PING_DEFAULT_INTERVAL,
34
+ PING_RANDOM_RANGE,
35
+ )
27
36
  from flwr.common.grpc import create_channel
28
- from flwr.common.logger import log, warn_experimental_feature
37
+ from flwr.common.logger import log
29
38
  from flwr.common.message import Message, Metadata
30
39
  from flwr.common.retry_invoker import RetryInvoker
31
40
  from flwr.common.serde import message_from_taskins, message_to_taskres
32
41
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
33
42
  CreateNodeRequest,
34
43
  DeleteNodeRequest,
44
+ PingRequest,
45
+ PingResponse,
35
46
  PullTaskInsRequest,
36
47
  PushTaskResRequest,
37
48
  )
@@ -39,9 +50,6 @@ from flwr.proto.fleet_pb2_grpc import FleetStub # pylint: disable=E0611
39
50
  from flwr.proto.node_pb2 import Node # pylint: disable=E0611
40
51
  from flwr.proto.task_pb2 import TaskIns # pylint: disable=E0611
41
52
 
42
- KEY_NODE = "node"
43
- KEY_METADATA = "in_message_metadata"
44
-
45
53
 
46
54
  def on_channel_state_change(channel_connectivity: str) -> None:
47
55
  """Log channel connectivity."""
@@ -49,7 +57,7 @@ def on_channel_state_change(channel_connectivity: str) -> None:
49
57
 
50
58
 
51
59
  @contextmanager
52
- def grpc_request_response(
60
+ def grpc_request_response( # pylint: disable=R0914, R0915
53
61
  server_address: str,
54
62
  insecure: bool,
55
63
  retry_invoker: RetryInvoker,
@@ -95,8 +103,6 @@ def grpc_request_response(
95
103
  create_node : Optional[Callable]
96
104
  delete_node : Optional[Callable]
97
105
  """
98
- warn_experimental_feature("`grpc-rere`")
99
-
100
106
  if isinstance(root_certificates, str):
101
107
  root_certificates = Path(root_certificates).read_bytes()
102
108
 
@@ -107,47 +113,81 @@ def grpc_request_response(
107
113
  max_message_length=max_message_length,
108
114
  )
109
115
  channel.subscribe(on_channel_state_change)
110
- stub = FleetStub(channel)
111
116
 
112
- # Necessary state to validate messages to be sent
113
- state: Dict[str, Optional[Metadata]] = {KEY_METADATA: None}
114
-
115
- # Enable create_node and delete_node to store node
116
- node_store: Dict[str, Optional[Node]] = {KEY_NODE: None}
117
+ # Shared variables for inner functions
118
+ stub = FleetStub(channel)
119
+ metadata: Optional[Metadata] = None
120
+ node: Optional[Node] = None
121
+ ping_thread: Optional[threading.Thread] = None
122
+ ping_stop_event = threading.Event()
117
123
 
118
124
  ###########################################################################
119
- # receive/send functions
125
+ # ping/create_node/delete_node/receive/send functions
120
126
  ###########################################################################
121
127
 
128
+ def ping() -> None:
129
+ # Get Node
130
+ if node is None:
131
+ log(ERROR, "Node instance missing")
132
+ return
133
+
134
+ # Construct the ping request
135
+ req = PingRequest(node=node, ping_interval=PING_DEFAULT_INTERVAL)
136
+
137
+ # Call FleetAPI
138
+ res: PingResponse = stub.Ping(req, timeout=PING_CALL_TIMEOUT)
139
+
140
+ # Check if success
141
+ if not res.success:
142
+ raise RuntimeError("Ping failed unexpectedly.")
143
+
144
+ # Wait
145
+ rd = random.uniform(*PING_RANDOM_RANGE)
146
+ next_interval: float = PING_DEFAULT_INTERVAL - PING_CALL_TIMEOUT
147
+ next_interval *= PING_BASE_MULTIPLIER + rd
148
+ if not ping_stop_event.is_set():
149
+ ping_stop_event.wait(next_interval)
150
+
122
151
  def create_node() -> None:
123
152
  """Set create_node."""
124
- create_node_request = CreateNodeRequest()
153
+ # Call FleetAPI
154
+ create_node_request = CreateNodeRequest(ping_interval=PING_DEFAULT_INTERVAL)
125
155
  create_node_response = retry_invoker.invoke(
126
156
  stub.CreateNode,
127
157
  request=create_node_request,
128
158
  )
129
- node_store[KEY_NODE] = create_node_response.node
159
+
160
+ # Remember the node and the ping-loop thread
161
+ nonlocal node, ping_thread
162
+ node = cast(Node, create_node_response.node)
163
+ ping_thread = start_ping_loop(ping, ping_stop_event)
130
164
 
131
165
  def delete_node() -> None:
132
166
  """Set delete_node."""
133
167
  # Get Node
134
- if node_store[KEY_NODE] is None:
168
+ nonlocal node
169
+ if node is None:
135
170
  log(ERROR, "Node instance missing")
136
171
  return
137
- node: Node = cast(Node, node_store[KEY_NODE])
138
172
 
173
+ # Stop the ping-loop thread
174
+ ping_stop_event.set()
175
+ if ping_thread is not None:
176
+ ping_thread.join()
177
+
178
+ # Call FleetAPI
139
179
  delete_node_request = DeleteNodeRequest(node=node)
140
180
  retry_invoker.invoke(stub.DeleteNode, request=delete_node_request)
141
181
 
142
- del node_store[KEY_NODE]
182
+ # Cleanup
183
+ node = None
143
184
 
144
185
  def receive() -> Optional[Message]:
145
186
  """Receive next task from server."""
146
187
  # Get Node
147
- if node_store[KEY_NODE] is None:
188
+ if node is None:
148
189
  log(ERROR, "Node instance missing")
149
190
  return None
150
- node: Node = cast(Node, node_store[KEY_NODE])
151
191
 
152
192
  # Request instructions (task) from server
153
193
  request = PullTaskInsRequest(node=node)
@@ -167,7 +207,8 @@ def grpc_request_response(
167
207
  in_message = message_from_taskins(task_ins) if task_ins else None
168
208
 
169
209
  # Remember `metadata` of the in message
170
- state[KEY_METADATA] = copy(in_message.metadata) if in_message else None
210
+ nonlocal metadata
211
+ metadata = copy(in_message.metadata) if in_message else None
171
212
 
172
213
  # Return the message if available
173
214
  return in_message
@@ -175,18 +216,18 @@ def grpc_request_response(
175
216
  def send(message: Message) -> None:
176
217
  """Send task result back to server."""
177
218
  # Get Node
178
- if node_store[KEY_NODE] is None:
219
+ if node is None:
179
220
  log(ERROR, "Node instance missing")
180
221
  return
181
222
 
182
- # Get incoming message
183
- in_metadata = state[KEY_METADATA]
184
- if in_metadata is None:
223
+ # Get the metadata of the incoming message
224
+ nonlocal metadata
225
+ if metadata is None:
185
226
  log(ERROR, "No current message")
186
227
  return
187
228
 
188
229
  # Validate out message
189
- if not validate_out_message(message, in_metadata):
230
+ if not validate_out_message(message, metadata):
190
231
  log(ERROR, "Invalid out message")
191
232
  return
192
233
 
@@ -197,7 +238,8 @@ def grpc_request_response(
197
238
  request = PushTaskResRequest(task_res_list=[task_res])
198
239
  _ = retry_invoker.invoke(stub.PushTaskRes, request)
199
240
 
200
- state[KEY_METADATA] = None
241
+ # Cleanup
242
+ metadata = None
201
243
 
202
244
  try:
203
245
  # Yield methods
@@ -0,0 +1,72 @@
1
+ # Copyright 2024 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
+ """Heartbeat utility functions."""
16
+
17
+
18
+ import threading
19
+ from typing import Callable
20
+
21
+ import grpc
22
+
23
+ from flwr.common.constant import PING_CALL_TIMEOUT
24
+ from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
25
+
26
+
27
+ def _ping_loop(ping_fn: Callable[[], None], stop_event: threading.Event) -> None:
28
+ def wait_fn(wait_time: float) -> None:
29
+ if not stop_event.is_set():
30
+ stop_event.wait(wait_time)
31
+
32
+ def on_backoff(state: RetryState) -> None:
33
+ err = state.exception
34
+ if not isinstance(err, grpc.RpcError):
35
+ return
36
+ status_code = err.code()
37
+ # If ping call timeout is triggered
38
+ if status_code == grpc.StatusCode.DEADLINE_EXCEEDED:
39
+ # Avoid long wait time.
40
+ if state.actual_wait is None:
41
+ return
42
+ state.actual_wait = max(state.actual_wait - PING_CALL_TIMEOUT, 0.0)
43
+
44
+ def wrapped_ping() -> None:
45
+ if not stop_event.is_set():
46
+ ping_fn()
47
+
48
+ retrier = RetryInvoker(
49
+ exponential,
50
+ grpc.RpcError,
51
+ max_tries=None,
52
+ max_time=None,
53
+ on_backoff=on_backoff,
54
+ wait_function=wait_fn,
55
+ )
56
+ while not stop_event.is_set():
57
+ retrier.invoke(wrapped_ping)
58
+
59
+
60
+ def start_ping_loop(
61
+ ping_fn: Callable[[], None], stop_event: threading.Event
62
+ ) -> threading.Thread:
63
+ """Start a ping loop in a separate thread.
64
+
65
+ This function initializes a new thread that runs a ping loop, allowing for
66
+ asynchronous ping operations. The loop can be terminated through the provided stop
67
+ event.
68
+ """
69
+ thread = threading.Thread(target=_ping_loop, args=(ping_fn, stop_event))
70
+ thread.start()
71
+
72
+ return thread