flwr-nightly 1.8.0.dev20240327__tar.gz → 1.8.0.dev20240401__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 (211) hide show
  1. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/PKG-INFO +1 -1
  2. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/pyproject.toml +2 -1
  3. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/app.py +37 -28
  4. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/client_app.py +7 -0
  5. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/grpc_rere_client/connection.py +70 -28
  6. flwr_nightly-1.8.0.dev20240401/src/py/flwr/client/heartbeat.py +72 -0
  7. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/rest_client/connection.py +101 -27
  8. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/constant.py +6 -0
  9. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/logger.py +4 -4
  10. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/message.py +38 -14
  11. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/retry_invoker.py +24 -13
  12. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/compat/driver_client_proxy.py +16 -0
  13. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/server_app.py +3 -0
  14. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/message_handler/message_handler.py +2 -1
  15. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/rest_rere/rest_api.py +28 -0
  16. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/vce/backend/raybackend.py +5 -4
  17. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/vce/vce_api.py +49 -25
  18. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/simulation/ray_transport/ray_actor.py +6 -2
  19. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/LICENSE +0 -0
  20. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/README.md +0 -0
  21. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/__init__.py +0 -0
  22. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/__init__.py +0 -0
  23. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/app.py +0 -0
  24. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/example.py +0 -0
  25. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/flower_toml.py +0 -0
  26. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/__init__.py +0 -0
  27. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/new.py +0 -0
  28. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/__init__.py +0 -0
  29. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/README.md.tpl +0 -0
  30. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/__init__.py +0 -0
  31. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/code/__init__.py +0 -0
  32. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/code/__init__.py.tpl +0 -0
  33. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -0
  34. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -0
  35. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -0
  36. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -0
  37. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -0
  38. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -0
  39. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -0
  40. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/flower.toml.tpl +0 -0
  41. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -0
  42. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -0
  43. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -0
  44. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -0
  45. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +0 -0
  46. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -0
  47. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/run/__init__.py +0 -0
  48. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/run/run.py +0 -0
  49. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/cli/utils.py +0 -0
  50. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/__init__.py +0 -0
  51. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/client.py +0 -0
  52. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/dpfedavg_numpy_client.py +0 -0
  53. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/grpc_client/__init__.py +0 -0
  54. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/grpc_client/connection.py +0 -0
  55. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/grpc_rere_client/__init__.py +0 -0
  56. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/message_handler/__init__.py +0 -0
  57. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/message_handler/message_handler.py +0 -0
  58. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/message_handler/task_handler.py +0 -0
  59. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/mod/__init__.py +0 -0
  60. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/mod/centraldp_mods.py +0 -0
  61. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/mod/comms_mods.py +0 -0
  62. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/mod/localdp_mod.py +0 -0
  63. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/mod/secure_aggregation/__init__.py +0 -0
  64. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/mod/secure_aggregation/secagg_mod.py +0 -0
  65. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/mod/secure_aggregation/secaggplus_mod.py +0 -0
  66. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/mod/utils.py +0 -0
  67. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/node_state.py +0 -0
  68. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/node_state_tests.py +0 -0
  69. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/numpy_client.py +0 -0
  70. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/rest_client/__init__.py +0 -0
  71. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/client/typing.py +0 -0
  72. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/__init__.py +0 -0
  73. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/address.py +0 -0
  74. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/context.py +0 -0
  75. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/date.py +0 -0
  76. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/differential_privacy.py +0 -0
  77. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/differential_privacy_constants.py +0 -0
  78. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/dp.py +0 -0
  79. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/exit_handlers.py +0 -0
  80. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/grpc.py +0 -0
  81. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/object_ref.py +0 -0
  82. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/parameter.py +0 -0
  83. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/pyproject.py +0 -0
  84. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/record/__init__.py +0 -0
  85. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/record/configsrecord.py +0 -0
  86. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/record/conversion_utils.py +0 -0
  87. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/record/metricsrecord.py +0 -0
  88. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/record/parametersrecord.py +0 -0
  89. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/record/recordset.py +0 -0
  90. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/record/typeddict.py +0 -0
  91. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/recordset_compat.py +0 -0
  92. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/secure_aggregation/__init__.py +0 -0
  93. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/secure_aggregation/crypto/__init__.py +0 -0
  94. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/secure_aggregation/crypto/shamir.py +0 -0
  95. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/secure_aggregation/crypto/symmetric_encryption.py +0 -0
  96. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/secure_aggregation/ndarrays_arithmetic.py +0 -0
  97. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/secure_aggregation/quantization.py +0 -0
  98. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/secure_aggregation/secaggplus_constants.py +0 -0
  99. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/secure_aggregation/secaggplus_utils.py +0 -0
  100. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/serde.py +0 -0
  101. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/telemetry.py +0 -0
  102. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/typing.py +0 -0
  103. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/common/version.py +0 -0
  104. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/__init__.py +0 -0
  105. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/driver_pb2.py +0 -0
  106. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/driver_pb2.pyi +0 -0
  107. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/driver_pb2_grpc.py +0 -0
  108. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/driver_pb2_grpc.pyi +0 -0
  109. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/error_pb2.py +0 -0
  110. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/error_pb2.pyi +0 -0
  111. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/error_pb2_grpc.py +0 -0
  112. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/error_pb2_grpc.pyi +0 -0
  113. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/fleet_pb2.py +0 -0
  114. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/fleet_pb2.pyi +0 -0
  115. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/fleet_pb2_grpc.py +0 -0
  116. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/fleet_pb2_grpc.pyi +0 -0
  117. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/node_pb2.py +0 -0
  118. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/node_pb2.pyi +0 -0
  119. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/node_pb2_grpc.py +0 -0
  120. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/node_pb2_grpc.pyi +0 -0
  121. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/recordset_pb2.py +0 -0
  122. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/recordset_pb2.pyi +0 -0
  123. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/recordset_pb2_grpc.py +0 -0
  124. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/recordset_pb2_grpc.pyi +0 -0
  125. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/task_pb2.py +0 -0
  126. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/task_pb2.pyi +0 -0
  127. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/task_pb2_grpc.py +0 -0
  128. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/task_pb2_grpc.pyi +0 -0
  129. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/transport_pb2.py +0 -0
  130. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/transport_pb2.pyi +0 -0
  131. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/transport_pb2_grpc.py +0 -0
  132. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/proto/transport_pb2_grpc.pyi +0 -0
  133. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/py.typed +0 -0
  134. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/__init__.py +0 -0
  135. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/app.py +0 -0
  136. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/client_manager.py +0 -0
  137. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/client_proxy.py +0 -0
  138. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/compat/__init__.py +0 -0
  139. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/compat/app.py +0 -0
  140. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/compat/app_utils.py +0 -0
  141. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/compat/legacy_context.py +0 -0
  142. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/criterion.py +0 -0
  143. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/driver/__init__.py +0 -0
  144. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/driver/driver.py +0 -0
  145. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/driver/grpc_driver.py +0 -0
  146. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/history.py +0 -0
  147. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/run_serverapp.py +0 -0
  148. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/server.py +0 -0
  149. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/server_config.py +0 -0
  150. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/__init__.py +0 -0
  151. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/aggregate.py +0 -0
  152. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/bulyan.py +0 -0
  153. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/dp_adaptive_clipping.py +0 -0
  154. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/dp_fixed_clipping.py +0 -0
  155. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/dpfedavg_adaptive.py +0 -0
  156. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/dpfedavg_fixed.py +0 -0
  157. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fault_tolerant_fedavg.py +0 -0
  158. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedadagrad.py +0 -0
  159. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedadam.py +0 -0
  160. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedavg.py +0 -0
  161. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedavg_android.py +0 -0
  162. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedavgm.py +0 -0
  163. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedmedian.py +0 -0
  164. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedopt.py +0 -0
  165. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedprox.py +0 -0
  166. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedtrimmedavg.py +0 -0
  167. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedxgb_bagging.py +0 -0
  168. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedxgb_cyclic.py +0 -0
  169. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedxgb_nn_avg.py +0 -0
  170. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/fedyogi.py +0 -0
  171. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/krum.py +0 -0
  172. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/qfedavg.py +0 -0
  173. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/strategy/strategy.py +0 -0
  174. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/__init__.py +0 -0
  175. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/driver/__init__.py +0 -0
  176. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/driver/driver_grpc.py +0 -0
  177. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/driver/driver_servicer.py +0 -0
  178. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/__init__.py +0 -0
  179. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/grpc_bidi/__init__.py +0 -0
  180. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +0 -0
  181. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +0 -0
  182. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +0 -0
  183. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +0 -0
  184. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/grpc_rere/__init__.py +0 -0
  185. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +0 -0
  186. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/message_handler/__init__.py +0 -0
  187. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/rest_rere/__init__.py +0 -0
  188. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/vce/__init__.py +0 -0
  189. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/vce/backend/__init__.py +0 -0
  190. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/fleet/vce/backend/backend.py +0 -0
  191. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/state/__init__.py +0 -0
  192. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/state/in_memory_state.py +0 -0
  193. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/state/sqlite_state.py +0 -0
  194. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/state/state.py +0 -0
  195. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/superlink/state/state_factory.py +0 -0
  196. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/typing.py +0 -0
  197. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/utils/__init__.py +0 -0
  198. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/utils/tensorboard.py +0 -0
  199. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/utils/validator.py +0 -0
  200. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/workflow/__init__.py +0 -0
  201. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/workflow/constant.py +0 -0
  202. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/workflow/default_workflows.py +0 -0
  203. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/workflow/secure_aggregation/__init__.py +0 -0
  204. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/workflow/secure_aggregation/secagg_workflow.py +0 -0
  205. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +0 -0
  206. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/simulation/__init__.py +0 -0
  207. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/simulation/app.py +0 -0
  208. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/simulation/ray_transport/__init__.py +0 -0
  209. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/simulation/ray_transport/ray_client_proxy.py +0 -0
  210. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/src/py/flwr/simulation/ray_transport/utils.py +0 -0
  211. {flwr_nightly-1.8.0.dev20240327 → flwr_nightly-1.8.0.dev20240401}/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.dev20240327
3
+ Version: 1.8.0.dev20240401
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-dev20240327"
7
+ version = "1.8.0-dev20240401"
8
8
  description = "Flower: A Friendly Federated Learning Framework"
9
9
  license = "Apache-2.0"
10
10
  authors = ["The Flower Authors <hello@flower.ai>"]
@@ -127,6 +127,7 @@ check-wheel-contents = "==0.4.0"
127
127
  GitPython = "==3.1.32"
128
128
  PyGithub = "==2.1.1"
129
129
  licensecheck = "==2024"
130
+ pre-commit = "==3.5.0"
130
131
 
131
132
  [tool.isort]
132
133
  line_length = 88
@@ -14,11 +14,10 @@
14
14
  # ==============================================================================
15
15
  """Flower client app."""
16
16
 
17
-
18
17
  import argparse
19
18
  import sys
20
19
  import time
21
- from logging import DEBUG, INFO, WARN
20
+ from logging import DEBUG, ERROR, INFO, WARN
22
21
  from pathlib import Path
23
22
  from typing import Callable, ContextManager, Optional, Tuple, Type, Union
24
23
 
@@ -37,7 +36,8 @@ from flwr.common.constant import (
37
36
  TRANSPORT_TYPES,
38
37
  )
39
38
  from flwr.common.exit_handlers import register_exit_handlers
40
- from flwr.common.logger import log, warn_deprecated_feature, warn_experimental_feature
39
+ from flwr.common.logger import log, warn_deprecated_feature
40
+ from flwr.common.message import Error
41
41
  from flwr.common.object_ref import load_app, validate
42
42
  from flwr.common.retry_invoker import RetryInvoker, exponential
43
43
 
@@ -385,8 +385,6 @@ def _start_client_internal(
385
385
  return ClientApp(client_fn=client_fn)
386
386
 
387
387
  load_client_app_fn = _load_client_app
388
- else:
389
- warn_experimental_feature("`load_client_app_fn`")
390
388
 
391
389
  # At this point, only `load_client_app_fn` should be used
392
390
  # Both `client` and `client_fn` must not be used directly
@@ -397,7 +395,7 @@ def _start_client_internal(
397
395
  )
398
396
 
399
397
  retry_invoker = RetryInvoker(
400
- wait_factory=exponential,
398
+ wait_gen_factory=exponential,
401
399
  recoverable_exceptions=connection_error_type,
402
400
  max_tries=max_retries,
403
401
  max_time=max_wait_time,
@@ -482,32 +480,43 @@ def _start_client_internal(
482
480
  # Retrieve context for this run
483
481
  context = node_state.retrieve_context(run_id=message.metadata.run_id)
484
482
 
485
- # Load ClientApp instance
486
- client_app: ClientApp = load_client_app_fn()
483
+ # Create an error reply message that will never be used to prevent
484
+ # the used-before-assignment linting error
485
+ reply_message = message.create_error_reply(
486
+ error=Error(code=0, reason="Unknown")
487
+ )
487
488
 
488
- # Handle task message
489
- out_message = client_app(message=message, context=context)
489
+ # Handle app loading and task message
490
+ try:
491
+ # Load ClientApp instance
492
+ client_app: ClientApp = load_client_app_fn()
490
493
 
491
- # Update node state
492
- node_state.update_context(
493
- run_id=message.metadata.run_id,
494
- context=context,
495
- )
494
+ reply_message = client_app(message=message, context=context)
495
+ # Update node state
496
+ node_state.update_context(
497
+ run_id=message.metadata.run_id,
498
+ context=context,
499
+ )
500
+ except Exception as ex: # pylint: disable=broad-exception-caught
501
+ log(ERROR, "ClientApp raised an exception", exc_info=ex)
502
+
503
+ # Legacy grpc-bidi
504
+ if transport in ["grpc-bidi", None]:
505
+ # Raise exception, crash process
506
+ raise ex
507
+
508
+ # Don't update/change NodeState
509
+
510
+ # Create error message
511
+ # Reason example: "<class 'ZeroDivisionError'>:<'division by zero'>"
512
+ reason = str(type(ex)) + ":<'" + str(ex) + "'>"
513
+ reply_message = message.create_error_reply(
514
+ error=Error(code=0, reason=reason)
515
+ )
496
516
 
497
517
  # Send
498
- send(out_message)
499
- log(
500
- INFO,
501
- "[RUN %s, ROUND %s]",
502
- out_message.metadata.run_id,
503
- out_message.metadata.group_id,
504
- )
505
- log(
506
- INFO,
507
- "Sent: %s reply to message %s",
508
- out_message.metadata.message_type,
509
- message.metadata.message_id,
510
- )
518
+ send(reply_message)
519
+ log(INFO, "Sent reply")
511
520
 
512
521
  # Unregister node
513
522
  if delete_node is not None:
@@ -23,6 +23,7 @@ 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
 
@@ -123,6 +124,8 @@ class ClientApp:
123
124
  if self._call:
124
125
  raise _registration_error(MessageType.TRAIN)
125
126
 
127
+ warn_preview_feature("ClientApp-register-train-function")
128
+
126
129
  # Register provided function with the ClientApp object
127
130
  # Wrap mods around the wrapped step function
128
131
  self._train = make_ffn(train_fn, self._mods)
@@ -151,6 +154,8 @@ class ClientApp:
151
154
  if self._call:
152
155
  raise _registration_error(MessageType.EVALUATE)
153
156
 
157
+ warn_preview_feature("ClientApp-register-evaluate-function")
158
+
154
159
  # Register provided function with the ClientApp object
155
160
  # Wrap mods around the wrapped step function
156
161
  self._evaluate = make_ffn(evaluate_fn, self._mods)
@@ -179,6 +184,8 @@ class ClientApp:
179
184
  if self._call:
180
185
  raise _registration_error(MessageType.QUERY)
181
186
 
187
+ warn_preview_feature("ClientApp-register-query-function")
188
+
182
189
  # Register provided function with the ClientApp object
183
190
  # Wrap mods around the wrapped step function
184
191
  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."""
153
+ # Call FleetAPI
124
154
  create_node_request = CreateNodeRequest()
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
@@ -15,16 +15,25 @@
15
15
  """Contextmanager for a REST request-response channel to the Flower server."""
16
16
 
17
17
 
18
+ import random
18
19
  import sys
20
+ import threading
19
21
  from contextlib import contextmanager
20
22
  from copy import copy
21
23
  from logging import ERROR, INFO, WARN
22
- from typing import Callable, Dict, Iterator, Optional, Tuple, Union, cast
24
+ from typing import Callable, Iterator, Optional, Tuple, Union
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
27
- from flwr.common.constant import MISSING_EXTRA_REST
30
+ from flwr.common.constant import (
31
+ MISSING_EXTRA_REST,
32
+ PING_BASE_MULTIPLIER,
33
+ PING_CALL_TIMEOUT,
34
+ PING_DEFAULT_INTERVAL,
35
+ PING_RANDOM_RANGE,
36
+ )
28
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
@@ -33,6 +42,8 @@ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
33
42
  CreateNodeRequest,
34
43
  CreateNodeResponse,
35
44
  DeleteNodeRequest,
45
+ PingRequest,
46
+ PingResponse,
36
47
  PullTaskInsRequest,
37
48
  PullTaskInsResponse,
38
49
  PushTaskResRequest,
@@ -47,19 +58,15 @@ except ModuleNotFoundError:
47
58
  sys.exit(MISSING_EXTRA_REST)
48
59
 
49
60
 
50
- KEY_NODE = "node"
51
- KEY_METADATA = "in_message_metadata"
52
-
53
-
54
61
  PATH_CREATE_NODE: str = "api/v0/fleet/create-node"
55
62
  PATH_DELETE_NODE: str = "api/v0/fleet/delete-node"
56
63
  PATH_PULL_TASK_INS: str = "api/v0/fleet/pull-task-ins"
57
64
  PATH_PUSH_TASK_RES: str = "api/v0/fleet/push-task-res"
65
+ PATH_PING: str = "api/v0/fleet/ping"
58
66
 
59
67
 
60
68
  @contextmanager
61
- # pylint: disable-next=too-many-statements
62
- def http_request_response(
69
+ def http_request_response( # pylint: disable=R0914, R0915
63
70
  server_address: str,
64
71
  insecure: bool, # pylint: disable=unused-argument
65
72
  retry_invoker: RetryInvoker,
@@ -127,16 +134,71 @@ def http_request_response(
127
134
  "must be provided as a string path to the client.",
128
135
  )
129
136
 
130
- # Necessary state to validate messages to be sent
131
- state: Dict[str, Optional[Metadata]] = {KEY_METADATA: None}
132
-
133
- # Enable create_node and delete_node to store node
134
- node_store: Dict[str, Optional[Node]] = {KEY_NODE: None}
137
+ # Shared variables for inner functions
138
+ metadata: Optional[Metadata] = None
139
+ node: Optional[Node] = None
140
+ ping_thread: Optional[threading.Thread] = None
141
+ ping_stop_event = threading.Event()
135
142
 
136
143
  ###########################################################################
137
- # receive/send functions
144
+ # ping/create_node/delete_node/receive/send functions
138
145
  ###########################################################################
139
146
 
147
+ def ping() -> None:
148
+ # Get Node
149
+ if node is None:
150
+ log(ERROR, "Node instance missing")
151
+ return
152
+
153
+ # Construct the ping request
154
+ req = PingRequest(node=node, ping_interval=PING_DEFAULT_INTERVAL)
155
+ req_bytes: bytes = req.SerializeToString()
156
+
157
+ # Send the request
158
+ res = requests.post(
159
+ url=f"{base_url}/{PATH_PING}",
160
+ headers={
161
+ "Accept": "application/protobuf",
162
+ "Content-Type": "application/protobuf",
163
+ },
164
+ data=req_bytes,
165
+ verify=verify,
166
+ timeout=PING_CALL_TIMEOUT,
167
+ )
168
+
169
+ # Check status code and headers
170
+ if res.status_code != 200:
171
+ return
172
+ if "content-type" not in res.headers:
173
+ log(
174
+ WARN,
175
+ "[Node] POST /%s: missing header `Content-Type`",
176
+ PATH_PULL_TASK_INS,
177
+ )
178
+ return
179
+ if res.headers["content-type"] != "application/protobuf":
180
+ log(
181
+ WARN,
182
+ "[Node] POST /%s: header `Content-Type` has wrong value",
183
+ PATH_PULL_TASK_INS,
184
+ )
185
+ return
186
+
187
+ # Deserialize ProtoBuf from bytes
188
+ ping_res = PingResponse()
189
+ ping_res.ParseFromString(res.content)
190
+
191
+ # Check if success
192
+ if not ping_res.success:
193
+ raise RuntimeError("Ping failed unexpectedly.")
194
+
195
+ # Wait
196
+ rd = random.uniform(*PING_RANDOM_RANGE)
197
+ next_interval: float = PING_DEFAULT_INTERVAL - PING_CALL_TIMEOUT
198
+ next_interval *= PING_BASE_MULTIPLIER + rd
199
+ if not ping_stop_event.is_set():
200
+ ping_stop_event.wait(next_interval)
201
+
140
202
  def create_node() -> None:
141
203
  """Set create_node."""
142
204
  create_node_req_proto = CreateNodeRequest()
@@ -175,15 +237,25 @@ def http_request_response(
175
237
  # Deserialize ProtoBuf from bytes
176
238
  create_node_response_proto = CreateNodeResponse()
177
239
  create_node_response_proto.ParseFromString(res.content)
178
- # pylint: disable-next=no-member
179
- node_store[KEY_NODE] = create_node_response_proto.node
240
+
241
+ # Remember the node and the ping-loop thread
242
+ nonlocal node, ping_thread
243
+ node = create_node_response_proto.node
244
+ ping_thread = start_ping_loop(ping, ping_stop_event)
180
245
 
181
246
  def delete_node() -> None:
182
247
  """Set delete_node."""
183
- if node_store[KEY_NODE] is None:
248
+ nonlocal node
249
+ if node is None:
184
250
  log(ERROR, "Node instance missing")
185
251
  return
186
- node: Node = cast(Node, node_store[KEY_NODE])
252
+
253
+ # Stop the ping-loop thread
254
+ ping_stop_event.set()
255
+ if ping_thread is not None:
256
+ ping_thread.join()
257
+
258
+ # Send DeleteNode request
187
259
  delete_node_req_proto = DeleteNodeRequest(node=node)
188
260
  delete_node_req_req_bytes: bytes = delete_node_req_proto.SerializeToString()
189
261
  res = retry_invoker.invoke(
@@ -215,13 +287,15 @@ def http_request_response(
215
287
  PATH_PULL_TASK_INS,
216
288
  )
217
289
 
290
+ # Cleanup
291
+ node = None
292
+
218
293
  def receive() -> Optional[Message]:
219
294
  """Receive next task from server."""
220
295
  # Get Node
221
- if node_store[KEY_NODE] is None:
296
+ if node is None:
222
297
  log(ERROR, "Node instance missing")
223
298
  return None
224
- node: Node = cast(Node, node_store[KEY_NODE])
225
299
 
226
300
  # Request instructions (task) from server
227
301
  pull_task_ins_req_proto = PullTaskInsRequest(node=node)
@@ -273,29 +347,29 @@ def http_request_response(
273
347
  task_ins = None
274
348
 
275
349
  # Return the Message if available
350
+ nonlocal metadata
276
351
  message = None
277
- state[KEY_METADATA] = None
278
352
  if task_ins is not None:
279
353
  message = message_from_taskins(task_ins)
280
- state[KEY_METADATA] = copy(message.metadata)
354
+ metadata = copy(message.metadata)
281
355
  log(INFO, "[Node] POST /%s: success", PATH_PULL_TASK_INS)
282
356
  return message
283
357
 
284
358
  def send(message: Message) -> None:
285
359
  """Send task result back to server."""
286
360
  # Get Node
287
- if node_store[KEY_NODE] is None:
361
+ if node is None:
288
362
  log(ERROR, "Node instance missing")
289
363
  return
290
364
 
291
365
  # Get incoming message
292
- in_metadata = state[KEY_METADATA]
293
- if in_metadata is None:
366
+ nonlocal metadata
367
+ if metadata is None:
294
368
  log(ERROR, "No current message")
295
369
  return
296
370
 
297
371
  # Validate out message
298
- if not validate_out_message(message, in_metadata):
372
+ if not validate_out_message(message, metadata):
299
373
  log(ERROR, "Invalid out message")
300
374
  return
301
375
 
@@ -321,7 +395,7 @@ def http_request_response(
321
395
  timeout=None,
322
396
  )
323
397
 
324
- state[KEY_METADATA] = None
398
+ metadata = None
325
399
 
326
400
  # Check status code and headers
327
401
  if res.status_code != 200:
@@ -36,6 +36,12 @@ TRANSPORT_TYPES = [
36
36
  TRANSPORT_TYPE_VCE,
37
37
  ]
38
38
 
39
+ # Constants for ping
40
+ PING_DEFAULT_INTERVAL = 30
41
+ PING_CALL_TIMEOUT = 5
42
+ PING_BASE_MULTIPLIER = 0.8
43
+ PING_RANDOM_RANGE = (-0.1, 0.1)
44
+
39
45
 
40
46
  class MessageType:
41
47
  """Message type."""