flwr-nightly 1.9.0.dev20240420__tar.gz → 1.9.0.dev20240507__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 (224) hide show
  1. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/PKG-INFO +3 -2
  2. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/README.md +1 -1
  3. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/pyproject.toml +4 -9
  4. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/app.py +2 -0
  5. flwr_nightly-1.9.0.dev20240507/src/py/flwr/cli/build.py +151 -0
  6. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/config_utils.py +18 -46
  7. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/new.py +42 -18
  8. flwr_nightly-1.9.0.dev20240507/src/py/flwr/cli/new/templates/app/code/client.mlx.py.tpl +70 -0
  9. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/code/client.pytorch.py.tpl +1 -1
  10. flwr_nightly-1.9.0.dev20240507/src/py/flwr/cli/new/templates/app/code/client.sklearn.py.tpl +94 -0
  11. flwr_nightly-1.9.0.dev20240507/src/py/flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +43 -0
  12. flwr_nightly-1.9.0.dev20240507/src/py/flwr/cli/new/templates/app/code/server.mlx.py.tpl +15 -0
  13. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/code/server.pytorch.py.tpl +1 -1
  14. flwr_nightly-1.9.0.dev20240420/src/py/flwr/cli/new/templates/app/code/server.tensorflow.py.tpl → flwr_nightly-1.9.0.dev20240507/src/py/flwr/cli/new/templates/app/code/server.sklearn.py.tpl +3 -5
  15. flwr_nightly-1.9.0.dev20240507/src/py/flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +27 -0
  16. flwr_nightly-1.9.0.dev20240507/src/py/flwr/cli/new/templates/app/code/task.mlx.py.tpl +89 -0
  17. flwr_nightly-1.9.0.dev20240507/src/py/flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +29 -0
  18. flwr_nightly-1.9.0.dev20240507/src/py/flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +28 -0
  19. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +7 -4
  20. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +7 -4
  21. flwr_nightly-1.9.0.dev20240507/src/py/flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +27 -0
  22. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +7 -4
  23. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/run/run.py +1 -1
  24. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/utils.py +18 -17
  25. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/__init__.py +1 -1
  26. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/app.py +17 -93
  27. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/grpc_client/connection.py +6 -1
  28. flwr_nightly-1.9.0.dev20240507/src/py/flwr/client/grpc_rere_client/client_interceptor.py +158 -0
  29. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/grpc_rere_client/connection.py +17 -2
  30. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/mod/centraldp_mods.py +4 -2
  31. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/mod/localdp_mod.py +9 -3
  32. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/rest_client/connection.py +5 -1
  33. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/supernode/__init__.py +2 -0
  34. flwr_nightly-1.9.0.dev20240507/src/py/flwr/client/supernode/app.py +281 -0
  35. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/grpc.py +5 -1
  36. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/logger.py +37 -4
  37. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/message.py +105 -86
  38. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/record/parametersrecord.py +0 -1
  39. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/record/recordset.py +17 -5
  40. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/secure_aggregation/crypto/symmetric_encryption.py +35 -1
  41. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/app.py +111 -1
  42. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/compat/app.py +2 -2
  43. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/compat/app_utils.py +1 -1
  44. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/compat/driver_client_proxy.py +27 -72
  45. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/driver/__init__.py +3 -0
  46. flwr_nightly-1.9.0.dev20240420/src/py/flwr/server/driver/driver.py → flwr_nightly-1.9.0.dev20240507/src/py/flwr/server/driver/grpc_driver.py +25 -80
  47. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/run_serverapp.py +18 -4
  48. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/dp_adaptive_clipping.py +5 -3
  49. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/dp_fixed_clipping.py +6 -3
  50. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/driver/driver_servicer.py +1 -1
  51. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +3 -1
  52. flwr_nightly-1.9.0.dev20240507/src/py/flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +215 -0
  53. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/vce/backend/raybackend.py +5 -5
  54. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/vce/vce_api.py +1 -1
  55. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/state/in_memory_state.py +76 -8
  56. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/state/sqlite_state.py +116 -11
  57. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/state/state.py +35 -3
  58. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/simulation/__init__.py +2 -2
  59. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/simulation/app.py +16 -1
  60. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/simulation/run_simulation.py +10 -7
  61. flwr_nightly-1.9.0.dev20240420/src/py/flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -57
  62. flwr_nightly-1.9.0.dev20240420/src/py/flwr/client/supernode/app.py +0 -107
  63. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/LICENSE +0 -0
  64. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/__init__.py +0 -0
  65. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/__init__.py +0 -0
  66. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/example.py +0 -0
  67. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/__init__.py +0 -0
  68. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/__init__.py +0 -0
  69. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/.gitignore.tpl +0 -0
  70. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/README.md.tpl +0 -0
  71. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/__init__.py +0 -0
  72. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/code/__init__.py +0 -0
  73. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/code/__init__.py.tpl +0 -0
  74. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -0
  75. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -0
  76. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -0
  77. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/cli/run/__init__.py +0 -0
  78. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/client.py +0 -0
  79. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/client_app.py +0 -0
  80. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/dpfedavg_numpy_client.py +0 -0
  81. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/grpc_client/__init__.py +0 -0
  82. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/grpc_rere_client/__init__.py +0 -0
  83. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/heartbeat.py +0 -0
  84. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/message_handler/__init__.py +0 -0
  85. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/message_handler/message_handler.py +0 -0
  86. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/message_handler/task_handler.py +0 -0
  87. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/mod/__init__.py +0 -0
  88. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/mod/comms_mods.py +0 -0
  89. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/mod/secure_aggregation/__init__.py +0 -0
  90. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/mod/secure_aggregation/secagg_mod.py +0 -0
  91. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/mod/secure_aggregation/secaggplus_mod.py +0 -0
  92. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/mod/utils.py +0 -0
  93. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/node_state.py +0 -0
  94. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/node_state_tests.py +0 -0
  95. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/numpy_client.py +0 -0
  96. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/rest_client/__init__.py +0 -0
  97. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/client/typing.py +0 -0
  98. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/__init__.py +0 -0
  99. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/address.py +0 -0
  100. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/constant.py +0 -0
  101. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/context.py +0 -0
  102. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/date.py +0 -0
  103. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/differential_privacy.py +0 -0
  104. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/differential_privacy_constants.py +0 -0
  105. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/dp.py +0 -0
  106. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/exit_handlers.py +0 -0
  107. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/object_ref.py +0 -0
  108. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/parameter.py +0 -0
  109. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/pyproject.py +0 -0
  110. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/record/__init__.py +0 -0
  111. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/record/configsrecord.py +0 -0
  112. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/record/conversion_utils.py +0 -0
  113. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/record/metricsrecord.py +0 -0
  114. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/record/typeddict.py +0 -0
  115. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/recordset_compat.py +0 -0
  116. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/retry_invoker.py +0 -0
  117. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/secure_aggregation/__init__.py +0 -0
  118. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/secure_aggregation/crypto/__init__.py +0 -0
  119. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/secure_aggregation/crypto/shamir.py +0 -0
  120. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/secure_aggregation/ndarrays_arithmetic.py +0 -0
  121. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/secure_aggregation/quantization.py +0 -0
  122. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/secure_aggregation/secaggplus_constants.py +0 -0
  123. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/secure_aggregation/secaggplus_utils.py +0 -0
  124. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/serde.py +0 -0
  125. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/telemetry.py +0 -0
  126. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/typing.py +0 -0
  127. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/common/version.py +0 -0
  128. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/__init__.py +0 -0
  129. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/driver_pb2.py +0 -0
  130. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/driver_pb2.pyi +0 -0
  131. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/driver_pb2_grpc.py +0 -0
  132. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/driver_pb2_grpc.pyi +0 -0
  133. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/error_pb2.py +0 -0
  134. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/error_pb2.pyi +0 -0
  135. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/error_pb2_grpc.py +0 -0
  136. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/error_pb2_grpc.pyi +0 -0
  137. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/fleet_pb2.py +0 -0
  138. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/fleet_pb2.pyi +0 -0
  139. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/fleet_pb2_grpc.py +0 -0
  140. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/fleet_pb2_grpc.pyi +0 -0
  141. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/node_pb2.py +0 -0
  142. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/node_pb2.pyi +0 -0
  143. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/node_pb2_grpc.py +0 -0
  144. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/node_pb2_grpc.pyi +0 -0
  145. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/recordset_pb2.py +0 -0
  146. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/recordset_pb2.pyi +0 -0
  147. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/recordset_pb2_grpc.py +0 -0
  148. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/recordset_pb2_grpc.pyi +0 -0
  149. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/task_pb2.py +0 -0
  150. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/task_pb2.pyi +0 -0
  151. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/task_pb2_grpc.py +0 -0
  152. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/task_pb2_grpc.pyi +0 -0
  153. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/transport_pb2.py +0 -0
  154. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/transport_pb2.pyi +0 -0
  155. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/transport_pb2_grpc.py +0 -0
  156. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/proto/transport_pb2_grpc.pyi +0 -0
  157. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/py.typed +0 -0
  158. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/__init__.py +0 -0
  159. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/client_manager.py +0 -0
  160. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/client_proxy.py +0 -0
  161. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/compat/__init__.py +0 -0
  162. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/compat/legacy_context.py +0 -0
  163. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/criterion.py +0 -0
  164. /flwr_nightly-1.9.0.dev20240420/src/py/flwr/server/driver/abc_driver.py → /flwr_nightly-1.9.0.dev20240507/src/py/flwr/server/driver/driver.py +0 -0
  165. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/history.py +0 -0
  166. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/server.py +0 -0
  167. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/server_app.py +0 -0
  168. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/server_config.py +0 -0
  169. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/__init__.py +0 -0
  170. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/aggregate.py +0 -0
  171. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/bulyan.py +0 -0
  172. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/dpfedavg_adaptive.py +0 -0
  173. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/dpfedavg_fixed.py +0 -0
  174. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fault_tolerant_fedavg.py +0 -0
  175. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedadagrad.py +0 -0
  176. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedadam.py +0 -0
  177. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedavg.py +0 -0
  178. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedavg_android.py +0 -0
  179. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedavgm.py +0 -0
  180. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedmedian.py +0 -0
  181. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedopt.py +0 -0
  182. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedprox.py +0 -0
  183. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedtrimmedavg.py +0 -0
  184. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedxgb_bagging.py +0 -0
  185. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedxgb_cyclic.py +0 -0
  186. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedxgb_nn_avg.py +0 -0
  187. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/fedyogi.py +0 -0
  188. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/krum.py +0 -0
  189. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/qfedavg.py +0 -0
  190. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/strategy/strategy.py +0 -0
  191. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/__init__.py +0 -0
  192. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/driver/__init__.py +0 -0
  193. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/driver/driver_grpc.py +0 -0
  194. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/__init__.py +0 -0
  195. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/grpc_bidi/__init__.py +0 -0
  196. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +0 -0
  197. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +0 -0
  198. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +0 -0
  199. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/grpc_rere/__init__.py +0 -0
  200. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +0 -0
  201. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/message_handler/__init__.py +0 -0
  202. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/message_handler/message_handler.py +0 -0
  203. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/rest_rere/__init__.py +0 -0
  204. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/rest_rere/rest_api.py +0 -0
  205. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/vce/__init__.py +0 -0
  206. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/vce/backend/__init__.py +0 -0
  207. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/fleet/vce/backend/backend.py +0 -0
  208. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/state/__init__.py +0 -0
  209. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/state/state_factory.py +0 -0
  210. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/superlink/state/utils.py +0 -0
  211. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/typing.py +0 -0
  212. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/utils/__init__.py +0 -0
  213. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/utils/tensorboard.py +0 -0
  214. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/utils/validator.py +0 -0
  215. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/workflow/__init__.py +0 -0
  216. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/workflow/constant.py +0 -0
  217. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/workflow/default_workflows.py +0 -0
  218. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/workflow/secure_aggregation/__init__.py +0 -0
  219. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/workflow/secure_aggregation/secagg_workflow.py +0 -0
  220. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +0 -0
  221. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/simulation/ray_transport/__init__.py +0 -0
  222. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/simulation/ray_transport/ray_actor.py +0 -0
  223. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/simulation/ray_transport/ray_client_proxy.py +0 -0
  224. {flwr_nightly-1.9.0.dev20240420 → flwr_nightly-1.9.0.dev20240507}/src/py/flwr/simulation/ray_transport/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.9.0.dev20240420
3
+ Version: 1.9.0.dev20240507
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -36,6 +36,7 @@ Requires-Dist: cryptography (>=42.0.4,<43.0.0)
36
36
  Requires-Dist: grpcio (>=1.60.0,<2.0.0)
37
37
  Requires-Dist: iterators (>=0.0.2,<0.0.3)
38
38
  Requires-Dist: numpy (>=1.21.0,<2.0.0)
39
+ Requires-Dist: pathspec (>=0.12.1,<0.13.0)
39
40
  Requires-Dist: protobuf (>=4.25.2,<5.0.0)
40
41
  Requires-Dist: pycryptodome (>=3.18.0,<4.0.0)
41
42
  Requires-Dist: ray (==2.6.3) ; (python_version >= "3.8" and python_version < "3.12") and (extra == "simulation")
@@ -193,7 +194,7 @@ Other [examples](https://github.com/adap/flower/tree/main/examples):
193
194
  - [PyTorch: From Centralized to Federated](https://github.com/adap/flower/tree/main/examples/pytorch-from-centralized-to-federated)
194
195
  - [Vertical FL](https://github.com/adap/flower/tree/main/examples/vertical-fl)
195
196
  - [Federated Finetuning of OpenAI's Whisper](https://github.com/adap/flower/tree/main/examples/whisper-federated-finetuning)
196
- - [Federated Finetuning of Large Language Model](https://github.com/adap/flower/tree/main/examples/fedllm-finetune)
197
+ - [Federated Finetuning of Large Language Model](https://github.com/adap/flower/tree/main/examples/llm-flowertune)
197
198
  - [Federated Finetuning of a Vision Transformer](https://github.com/adap/flower/tree/main/examples/vit-finetune)
198
199
  - [Advanced Flower with TensorFlow/Keras](https://github.com/adap/flower/tree/main/examples/advanced-tensorflow)
199
200
  - [Advanced Flower with PyTorch](https://github.com/adap/flower/tree/main/examples/advanced-pytorch)
@@ -143,7 +143,7 @@ Other [examples](https://github.com/adap/flower/tree/main/examples):
143
143
  - [PyTorch: From Centralized to Federated](https://github.com/adap/flower/tree/main/examples/pytorch-from-centralized-to-federated)
144
144
  - [Vertical FL](https://github.com/adap/flower/tree/main/examples/vertical-fl)
145
145
  - [Federated Finetuning of OpenAI's Whisper](https://github.com/adap/flower/tree/main/examples/whisper-federated-finetuning)
146
- - [Federated Finetuning of Large Language Model](https://github.com/adap/flower/tree/main/examples/fedllm-finetune)
146
+ - [Federated Finetuning of Large Language Model](https://github.com/adap/flower/tree/main/examples/llm-flowertune)
147
147
  - [Federated Finetuning of a Vision Transformer](https://github.com/adap/flower/tree/main/examples/vit-finetune)
148
148
  - [Advanced Flower with TensorFlow/Keras](https://github.com/adap/flower/tree/main/examples/advanced-tensorflow)
149
149
  - [Advanced Flower with PyTorch](https://github.com/adap/flower/tree/main/examples/advanced-pytorch)
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "flwr-nightly"
7
- version = "1.9.0.dev20240420"
7
+ version = "1.9.0.dev20240507"
8
8
  description = "Flower: A Friendly Federated Learning Framework"
9
9
  license = "Apache-2.0"
10
10
  authors = ["The Flower Authors <hello@flower.ai>"]
@@ -59,7 +59,7 @@ flower-superlink = "flwr.server:run_superlink"
59
59
  flower-supernode = "flwr.client:run_supernode"
60
60
  flower-client-app = "flwr.client:run_client_app"
61
61
  flower-server-app = "flwr.server:run_server_app"
62
- flower-simulation = "flwr.simulation:run_simulation_from_cli"
62
+ flower-simulation = "flwr.simulation.run_simulation:run_simulation_from_cli"
63
63
 
64
64
  [tool.poetry.dependencies]
65
65
  python = "^3.8"
@@ -72,6 +72,7 @@ pycryptodome = "^3.18.0"
72
72
  iterators = "^0.0.2"
73
73
  typer = { version = "^0.9.0", extras=["all"] }
74
74
  tomli = "^2.0.1"
75
+ pathspec = "^0.12.1"
75
76
  # Optional dependencies (Simulation Engine)
76
77
  ray = { version = "==2.6.3", optional = true, python = ">=3.8,<3.12" }
77
78
  # Optional dependencies (REST transport layer)
@@ -136,7 +137,7 @@ multi_line_output = 3
136
137
  include_trailing_comma = true
137
138
  force_grid_wrap = 0
138
139
  use_parentheses = true
139
- known_first_party = ["flwr", "flwr_experimental", "flwr_tool"]
140
+ known_first_party = ["flwr", "flwr_tool"]
140
141
 
141
142
  [tool.black]
142
143
  line-length = 88
@@ -170,12 +171,6 @@ plugins = [
170
171
  ignore_missing_imports = true
171
172
  strict = true
172
173
 
173
- [[tool.mypy.overrides]]
174
- module = [
175
- "flwr_experimental.*",
176
- ]
177
- ignore_errors = true
178
-
179
174
  [[tool.mypy.overrides]]
180
175
  module = [
181
176
  "importlib.metadata.*",
@@ -16,6 +16,7 @@
16
16
 
17
17
  import typer
18
18
 
19
+ from .build import build
19
20
  from .example import example
20
21
  from .new import new
21
22
  from .run import run
@@ -32,6 +33,7 @@ app = typer.Typer(
32
33
  app.command()(new)
33
34
  app.command()(example)
34
35
  app.command()(run)
36
+ app.command()(build)
35
37
 
36
38
  if __name__ == "__main__":
37
39
  app()
@@ -0,0 +1,151 @@
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
+ """Flower command line interface `build` command."""
16
+
17
+ import hashlib
18
+ import os
19
+ import zipfile
20
+ from pathlib import Path
21
+ from typing import Optional
22
+
23
+ import pathspec
24
+ import typer
25
+ from typing_extensions import Annotated
26
+
27
+ from .config_utils import load_and_validate_with_defaults
28
+ from .utils import is_valid_project_name
29
+
30
+
31
+ # pylint: disable=too-many-locals
32
+ def build(
33
+ directory: Annotated[
34
+ Optional[Path],
35
+ typer.Option(help="The Flower project directory to bundle into a FAB"),
36
+ ] = None,
37
+ ) -> None:
38
+ """Build a Flower project into a Flower App Bundle (FAB).
39
+
40
+ You can run `flwr build` without any argument to bundle the current directory:
41
+
42
+ `flwr build`
43
+
44
+ You can also build a specific directory:
45
+
46
+ `flwr build --directory ./projects/flower-hello-world`
47
+ """
48
+ if directory is None:
49
+ directory = Path.cwd()
50
+
51
+ directory = directory.resolve()
52
+ if not directory.is_dir():
53
+ typer.secho(
54
+ f"❌ The path {directory} is not a valid directory.",
55
+ fg=typer.colors.RED,
56
+ bold=True,
57
+ )
58
+ raise typer.Exit(code=1)
59
+
60
+ if not is_valid_project_name(directory.name):
61
+ typer.secho(
62
+ f"❌ The project name {directory.name} is invalid, "
63
+ "a valid project name must start with a letter or an underscore, "
64
+ "and can only contain letters, digits, and underscores.",
65
+ fg=typer.colors.RED,
66
+ bold=True,
67
+ )
68
+ raise typer.Exit(code=1)
69
+
70
+ conf, errors, warnings = load_and_validate_with_defaults(
71
+ directory / "pyproject.toml"
72
+ )
73
+ if conf is None:
74
+ typer.secho(
75
+ "Project configuration could not be loaded.\npyproject.toml is invalid:\n"
76
+ + "\n".join([f"- {line}" for line in errors]),
77
+ fg=typer.colors.RED,
78
+ bold=True,
79
+ )
80
+ raise typer.Exit(code=1)
81
+
82
+ if warnings:
83
+ typer.secho(
84
+ "Project configuration is missing the following "
85
+ "recommended properties:\n" + "\n".join([f"- {line}" for line in warnings]),
86
+ fg=typer.colors.RED,
87
+ bold=True,
88
+ )
89
+
90
+ # Load .gitignore rules if present
91
+ ignore_spec = _load_gitignore(directory)
92
+
93
+ # Set the name of the zip file
94
+ fab_filename = (
95
+ f"{conf['flower']['publisher']}"
96
+ f".{directory.name}"
97
+ f".{conf['project']['version'].replace('.', '-')}.fab"
98
+ )
99
+ list_file_content = ""
100
+
101
+ allowed_extensions = {".py", ".toml", ".md"}
102
+
103
+ with zipfile.ZipFile(fab_filename, "w", zipfile.ZIP_DEFLATED) as fab_file:
104
+ for root, _, files in os.walk(directory, topdown=True):
105
+ # Filter directories and files based on .gitignore
106
+ files = [
107
+ f
108
+ for f in files
109
+ if not ignore_spec.match_file(Path(root) / f)
110
+ and f != fab_filename
111
+ and Path(f).suffix in allowed_extensions
112
+ ]
113
+
114
+ for file in files:
115
+ file_path = Path(root) / file
116
+ archive_path = file_path.relative_to(directory)
117
+ fab_file.write(file_path, archive_path)
118
+
119
+ # Calculate file info
120
+ sha256_hash = _get_sha256_hash(file_path)
121
+ file_size_bits = os.path.getsize(file_path) * 8 # size in bits
122
+ list_file_content += f"{archive_path},{sha256_hash},{file_size_bits}\n"
123
+
124
+ # Add CONTENT and CONTENT.jwt to the zip file
125
+ fab_file.writestr(".info/CONTENT", list_file_content)
126
+
127
+ typer.secho(
128
+ f"🎊 Successfully built {fab_filename}.", fg=typer.colors.GREEN, bold=True
129
+ )
130
+
131
+
132
+ def _get_sha256_hash(file_path: Path) -> str:
133
+ """Calculate the SHA-256 hash of a file."""
134
+ sha256 = hashlib.sha256()
135
+ with open(file_path, "rb") as f:
136
+ while True:
137
+ data = f.read(65536) # Read in 64kB blocks
138
+ if not data:
139
+ break
140
+ sha256.update(data)
141
+ return sha256.hexdigest()
142
+
143
+
144
+ def _load_gitignore(directory: Path) -> pathspec.PathSpec:
145
+ """Load and parse .gitignore file, returning a pathspec."""
146
+ gitignore_path = directory / ".gitignore"
147
+ patterns = ["__pycache__/"] # Default pattern
148
+ if gitignore_path.exists():
149
+ with open(gitignore_path, encoding="UTF-8") as file:
150
+ patterns.extend(file.readlines())
151
+ return pathspec.PathSpec.from_lines("gitwildmatch", patterns)
@@ -14,49 +14,16 @@
14
14
  # ==============================================================================
15
15
  """Utility to validate the `pyproject.toml` file."""
16
16
 
17
- import os
18
17
  from pathlib import Path
19
18
  from typing import Any, Dict, List, Optional, Tuple
20
19
 
21
20
  import tomli
22
- import typer
23
21
 
24
22
  from flwr.common import object_ref
25
23
 
26
24
 
27
- def validate_project_dir(project_dir: Path) -> Optional[Dict[str, Any]]:
28
- """Check if a Flower App directory is valid."""
29
- config = load(str(project_dir / "pyproject.toml"))
30
- if config is None:
31
- typer.secho(
32
- "❌ Project configuration could not be loaded. "
33
- "`pyproject.toml` does not exist.",
34
- fg=typer.colors.RED,
35
- bold=True,
36
- )
37
- return None
38
-
39
- if not validate(config):
40
- typer.secho(
41
- "❌ Project configuration is invalid.",
42
- fg=typer.colors.RED,
43
- bold=True,
44
- )
45
- return None
46
-
47
- if "publisher" not in config["flower"]:
48
- typer.secho(
49
- "❌ Project configuration is missing required `publisher` field.",
50
- fg=typer.colors.RED,
51
- bold=True,
52
- )
53
- return None
54
-
55
- return config
56
-
57
-
58
25
  def load_and_validate_with_defaults(
59
- path: Optional[str] = None,
26
+ path: Optional[Path] = None,
60
27
  ) -> Tuple[Optional[Dict[str, Any]], List[str], List[str]]:
61
28
  """Load and validate pyproject.toml as dict.
62
29
 
@@ -70,7 +37,8 @@ def load_and_validate_with_defaults(
70
37
 
71
38
  if config is None:
72
39
  errors = [
73
- "Project configuration could not be loaded. pyproject.toml does not exist."
40
+ "Project configuration could not be loaded. "
41
+ "`pyproject.toml` does not exist."
74
42
  ]
75
43
  return (None, errors, [])
76
44
 
@@ -90,22 +58,23 @@ def load_and_validate_with_defaults(
90
58
  return (config, errors, warnings)
91
59
 
92
60
 
93
- def load(path: Optional[str] = None) -> Optional[Dict[str, Any]]:
61
+ def load(path: Optional[Path] = None) -> Optional[Dict[str, Any]]:
94
62
  """Load pyproject.toml and return as dict."""
95
63
  if path is None:
96
- cur_dir = os.getcwd()
97
- toml_path = os.path.join(cur_dir, "pyproject.toml")
64
+ cur_dir = Path.cwd()
65
+ toml_path = cur_dir / "pyproject.toml"
98
66
  else:
99
67
  toml_path = path
100
68
 
101
- if not os.path.isfile(toml_path):
69
+ if not toml_path.is_file():
102
70
  return None
103
71
 
104
- with open(toml_path, encoding="utf-8") as toml_file:
72
+ with toml_path.open(encoding="utf-8") as toml_file:
105
73
  data = tomli.loads(toml_file.read())
106
74
  return data
107
75
 
108
76
 
77
+ # pylint: disable=too-many-branches
109
78
  def validate_fields(config: Dict[str, Any]) -> Tuple[bool, List[str], List[str]]:
110
79
  """Validate pyproject.toml fields."""
111
80
  errors = []
@@ -127,13 +96,16 @@ def validate_fields(config: Dict[str, Any]) -> Tuple[bool, List[str], List[str]]
127
96
 
128
97
  if "flower" not in config:
129
98
  errors.append("Missing [flower] section")
130
- elif "components" not in config["flower"]:
131
- errors.append("Missing [flower.components] section")
132
99
  else:
133
- if "serverapp" not in config["flower"]["components"]:
134
- errors.append('Property "serverapp" missing in [flower.components]')
135
- if "clientapp" not in config["flower"]["components"]:
136
- errors.append('Property "clientapp" missing in [flower.components]')
100
+ if "publisher" not in config["flower"]:
101
+ errors.append('Property "publisher" missing in [flower]')
102
+ if "components" not in config["flower"]:
103
+ errors.append("Missing [flower.components] section")
104
+ else:
105
+ if "serverapp" not in config["flower"]["components"]:
106
+ errors.append('Property "serverapp" missing in [flower.components]')
107
+ if "clientapp" not in config["flower"]["components"]:
108
+ errors.append('Property "clientapp" missing in [flower.components]')
137
109
 
138
110
  return len(errors) == 0, errors, warnings
139
111
 
@@ -15,6 +15,7 @@
15
15
  """Flower command line interface `new` command."""
16
16
 
17
17
  import os
18
+ import re
18
19
  from enum import Enum
19
20
  from string import Template
20
21
  from typing import Dict, Optional
@@ -36,6 +37,8 @@ class MlFramework(str, Enum):
36
37
  NUMPY = "NumPy"
37
38
  PYTORCH = "PyTorch"
38
39
  TENSORFLOW = "TensorFlow"
40
+ MLX = "MLX"
41
+ SKLEARN = "sklearn"
39
42
 
40
43
 
41
44
  class TemplateNotFound(Exception):
@@ -85,32 +88,31 @@ def new(
85
88
  Optional[MlFramework],
86
89
  typer.Option(case_sensitive=False, help="The ML framework to use"),
87
90
  ] = None,
91
+ username: Annotated[
92
+ Optional[str],
93
+ typer.Option(case_sensitive=False, help="The Flower username of the author"),
94
+ ] = None,
88
95
  ) -> None:
89
96
  """Create new Flower project."""
90
97
  if project_name is None:
91
- project_name = prompt_text("Please provide project name")
98
+ project_name = prompt_text("Please provide the project name")
92
99
  if not is_valid_project_name(project_name):
93
100
  project_name = prompt_text(
94
101
  "Please provide a name that only contains "
95
- "characters in {'_', 'a-zA-Z', '0-9'}",
102
+ "characters in {'-', a-zA-Z', '0-9'}",
96
103
  predicate=is_valid_project_name,
97
104
  default=sanitize_project_name(project_name),
98
105
  )
99
106
 
100
- print(
101
- typer.style(
102
- f"🔨 Creating Flower project {project_name}...",
103
- fg=typer.colors.GREEN,
104
- bold=True,
105
- )
106
- )
107
+ if username is None:
108
+ username = prompt_text("Please provide your Flower username")
107
109
 
108
110
  if framework is not None:
109
111
  framework_str = str(framework.value)
110
112
  else:
111
113
  framework_value = prompt_options(
112
114
  "Please select ML framework by typing in the number",
113
- [mlf.value for mlf in MlFramework],
115
+ sorted([mlf.value for mlf in MlFramework]),
114
116
  )
115
117
  selected_value = [
116
118
  name
@@ -121,29 +123,51 @@ def new(
121
123
 
122
124
  framework_str = framework_str.lower()
123
125
 
126
+ print(
127
+ typer.style(
128
+ f"\n🔨 Creating Flower project {project_name}...",
129
+ fg=typer.colors.GREEN,
130
+ bold=True,
131
+ )
132
+ )
133
+
124
134
  # Set project directory path
125
135
  cwd = os.getcwd()
126
- pnl = project_name.lower()
127
- project_dir = os.path.join(cwd, pnl)
136
+ package_name = re.sub(r"[-_.]+", "-", project_name).lower()
137
+ import_name = package_name.replace("-", "_")
138
+ project_dir = os.path.join(cwd, package_name)
128
139
 
129
140
  # List of files to render
130
141
  files = {
131
142
  ".gitignore": {"template": "app/.gitignore.tpl"},
132
143
  "README.md": {"template": "app/README.md.tpl"},
133
144
  "pyproject.toml": {"template": f"app/pyproject.{framework_str}.toml.tpl"},
134
- f"{pnl}/__init__.py": {"template": "app/code/__init__.py.tpl"},
135
- f"{pnl}/server.py": {"template": f"app/code/server.{framework_str}.py.tpl"},
136
- f"{pnl}/client.py": {"template": f"app/code/client.{framework_str}.py.tpl"},
145
+ f"{import_name}/__init__.py": {"template": "app/code/__init__.py.tpl"},
146
+ f"{import_name}/server.py": {
147
+ "template": f"app/code/server.{framework_str}.py.tpl"
148
+ },
149
+ f"{import_name}/client.py": {
150
+ "template": f"app/code/client.{framework_str}.py.tpl"
151
+ },
137
152
  }
138
153
 
139
154
  # Depending on the framework, generate task.py file
140
155
  frameworks_with_tasks = [
141
156
  MlFramework.PYTORCH.value.lower(),
157
+ MlFramework.MLX.value.lower(),
158
+ MlFramework.TENSORFLOW.value.lower(),
142
159
  ]
143
160
  if framework_str in frameworks_with_tasks:
144
- files[f"{pnl}/task.py"] = {"template": f"app/code/task.{framework_str}.py.tpl"}
145
-
146
- context = {"project_name": project_name}
161
+ files[f"{import_name}/task.py"] = {
162
+ "template": f"app/code/task.{framework_str}.py.tpl"
163
+ }
164
+
165
+ context = {
166
+ "project_name": project_name,
167
+ "package_name": package_name,
168
+ "import_name": import_name.replace("-", "_"),
169
+ "username": username,
170
+ }
147
171
 
148
172
  for file_path, value in files.items():
149
173
  render_and_create(
@@ -0,0 +1,70 @@
1
+ """$project_name: A Flower / MLX app."""
2
+
3
+ import mlx.core as mx
4
+ import mlx.nn as nn
5
+ import mlx.optimizers as optim
6
+ from flwr.client import NumPyClient, ClientApp
7
+
8
+ from $import_name.task import (
9
+ batch_iterate,
10
+ eval_fn,
11
+ get_params,
12
+ load_data,
13
+ loss_fn,
14
+ set_params,
15
+ MLP,
16
+ )
17
+
18
+
19
+ # Define Flower Client and client_fn
20
+ class FlowerClient(NumPyClient):
21
+ def __init__(self, data):
22
+ num_layers = 2
23
+ hidden_dim = 32
24
+ num_classes = 10
25
+ batch_size = 256
26
+ num_epochs = 1
27
+ learning_rate = 1e-1
28
+
29
+ self.train_images, self.train_labels, self.test_images, self.test_labels = data
30
+ self.model = MLP(num_layers, self.train_images.shape[-1], hidden_dim, num_classes)
31
+ self.optimizer = optim.SGD(learning_rate=learning_rate)
32
+ self.loss_and_grad_fn = nn.value_and_grad(self.model, loss_fn)
33
+ self.num_epochs = num_epochs
34
+ self.batch_size = batch_size
35
+
36
+ def get_parameters(self, config):
37
+ return get_params(self.model)
38
+
39
+ def set_parameters(self, parameters):
40
+ set_params(self.model, parameters)
41
+
42
+ def fit(self, parameters, config):
43
+ self.set_parameters(parameters)
44
+ for _ in range(self.num_epochs):
45
+ for X, y in batch_iterate(
46
+ self.batch_size, self.train_images, self.train_labels
47
+ ):
48
+ _, grads = self.loss_and_grad_fn(self.model, X, y)
49
+ self.optimizer.update(self.model, grads)
50
+ mx.eval(self.model.parameters(), self.optimizer.state)
51
+ return self.get_parameters(config={}), len(self.train_images), {}
52
+
53
+ def evaluate(self, parameters, config):
54
+ self.set_parameters(parameters)
55
+ accuracy = eval_fn(self.model, self.test_images, self.test_labels)
56
+ loss = loss_fn(self.model, self.test_images, self.test_labels)
57
+ return loss.item(), len(self.test_images), {"accuracy": accuracy.item()}
58
+
59
+
60
+ def client_fn(cid):
61
+ data = load_data(int(cid), 2)
62
+
63
+ # Return Client instance
64
+ return FlowerClient(data).to_client()
65
+
66
+
67
+ # Flower ClientApp
68
+ app = ClientApp(
69
+ client_fn,
70
+ )
@@ -2,7 +2,7 @@
2
2
 
3
3
  from flwr.client import NumPyClient, ClientApp
4
4
 
5
- from $project_name.task import (
5
+ from $import_name.task import (
6
6
  Net,
7
7
  DEVICE,
8
8
  load_data,
@@ -0,0 +1,94 @@
1
+ """$project_name: A Flower / Scikit-Learn app."""
2
+
3
+ import warnings
4
+
5
+ import numpy as np
6
+ from flwr.client import NumPyClient, ClientApp
7
+ from flwr_datasets import FederatedDataset
8
+ from sklearn.linear_model import LogisticRegression
9
+ from sklearn.metrics import log_loss
10
+
11
+
12
+ def get_model_parameters(model):
13
+ if model.fit_intercept:
14
+ params = [
15
+ model.coef_,
16
+ model.intercept_,
17
+ ]
18
+ else:
19
+ params = [model.coef_]
20
+ return params
21
+
22
+
23
+ def set_model_params(model, params):
24
+ model.coef_ = params[0]
25
+ if model.fit_intercept:
26
+ model.intercept_ = params[1]
27
+ return model
28
+
29
+
30
+ def set_initial_params(model):
31
+ n_classes = 10 # MNIST has 10 classes
32
+ n_features = 784 # Number of features in dataset
33
+ model.classes_ = np.array([i for i in range(10)])
34
+
35
+ model.coef_ = np.zeros((n_classes, n_features))
36
+ if model.fit_intercept:
37
+ model.intercept_ = np.zeros((n_classes,))
38
+
39
+
40
+ class FlowerClient(NumPyClient):
41
+ def __init__(self, model, X_train, X_test, y_train, y_test):
42
+ self.model = model
43
+ self.X_train = X_train
44
+ self.X_test = X_test
45
+ self.y_train = y_train
46
+ self.y_test = y_test
47
+
48
+ def get_parameters(self, config):
49
+ return get_model_parameters(self.model)
50
+
51
+ def fit(self, parameters, config):
52
+ set_model_params(self.model, parameters)
53
+
54
+ # Ignore convergence failure due to low local epochs
55
+ with warnings.catch_warnings():
56
+ warnings.simplefilter("ignore")
57
+ self.model.fit(self.X_train, self.y_train)
58
+
59
+ return get_model_parameters(self.model), len(self.X_train), {}
60
+
61
+ def evaluate(self, parameters, config):
62
+ set_model_params(self.model, parameters)
63
+
64
+ loss = log_loss(self.y_test, self.model.predict_proba(self.X_test))
65
+ accuracy = self.model.score(self.X_test, self.y_test)
66
+
67
+ return loss, len(self.X_test), {"accuracy": accuracy}
68
+
69
+ fds = FederatedDataset(dataset="mnist", partitioners={"train": 2})
70
+
71
+ def client_fn(cid: str):
72
+ dataset = fds.load_partition(int(cid), "train").with_format("numpy")
73
+
74
+ X, y = dataset["image"].reshape((len(dataset), -1)), dataset["label"]
75
+
76
+ # Split the on edge data: 80% train, 20% test
77
+ X_train, X_test = X[: int(0.8 * len(X))], X[int(0.8 * len(X)) :]
78
+ y_train, y_test = y[: int(0.8 * len(y))], y[int(0.8 * len(y)) :]
79
+
80
+ # Create LogisticRegression Model
81
+ model = LogisticRegression(
82
+ penalty="l2",
83
+ max_iter=1, # local epoch
84
+ warm_start=True, # prevent refreshing weights when fitting
85
+ )
86
+
87
+ # Setting initial parameters, akin to model.compile for keras models
88
+ set_initial_params(model)
89
+
90
+ return FlowerClient(model, X_train, X_test, y_train, y_test).to_client()
91
+
92
+
93
+ # Flower ClientApp
94
+ app = ClientApp(client_fn=client_fn)