flwr 1.17.0__py3-none-any.whl → 1.18.0__py3-none-any.whl

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.
Files changed (223) hide show
  1. flwr/__init__.py +1 -1
  2. flwr/cli/__init__.py +1 -1
  3. flwr/cli/app.py +21 -2
  4. flwr/cli/build.py +1 -1
  5. flwr/cli/cli_user_auth_interceptor.py +1 -1
  6. flwr/cli/config_utils.py +53 -17
  7. flwr/cli/example.py +1 -1
  8. flwr/cli/install.py +1 -1
  9. flwr/cli/log.py +1 -1
  10. flwr/cli/login/__init__.py +1 -1
  11. flwr/cli/login/login.py +12 -1
  12. flwr/cli/ls.py +1 -1
  13. flwr/cli/new/__init__.py +1 -1
  14. flwr/cli/new/new.py +4 -4
  15. flwr/cli/new/templates/__init__.py +1 -1
  16. flwr/cli/new/templates/app/__init__.py +1 -1
  17. flwr/cli/new/templates/app/code/__init__.py +1 -1
  18. flwr/cli/new/templates/app/code/flwr_tune/__init__.py +1 -1
  19. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +4 -4
  20. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +1 -1
  21. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
  22. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
  23. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
  24. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
  25. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
  26. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
  27. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
  28. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
  29. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
  30. flwr/cli/run/__init__.py +1 -1
  31. flwr/cli/run/run.py +1 -1
  32. flwr/cli/stop.py +1 -1
  33. flwr/cli/utils.py +11 -12
  34. flwr/client/__init__.py +1 -1
  35. flwr/client/app.py +52 -52
  36. flwr/client/client.py +1 -1
  37. flwr/client/client_app.py +139 -137
  38. flwr/client/clientapp/__init__.py +1 -1
  39. flwr/client/clientapp/app.py +1 -1
  40. flwr/client/clientapp/clientappio_servicer.py +1 -1
  41. flwr/client/clientapp/utils.py +1 -1
  42. flwr/client/dpfedavg_numpy_client.py +1 -1
  43. flwr/client/grpc_adapter_client/__init__.py +1 -1
  44. flwr/client/grpc_adapter_client/connection.py +1 -1
  45. flwr/client/grpc_client/__init__.py +1 -1
  46. flwr/client/grpc_client/connection.py +13 -13
  47. flwr/client/grpc_rere_client/__init__.py +1 -1
  48. flwr/client/grpc_rere_client/client_interceptor.py +1 -1
  49. flwr/client/grpc_rere_client/connection.py +1 -1
  50. flwr/client/grpc_rere_client/grpc_adapter.py +1 -1
  51. flwr/client/heartbeat.py +1 -1
  52. flwr/client/message_handler/__init__.py +1 -1
  53. flwr/client/message_handler/message_handler.py +1 -1
  54. flwr/client/mod/__init__.py +1 -1
  55. flwr/client/mod/centraldp_mods.py +1 -1
  56. flwr/client/mod/comms_mods.py +3 -3
  57. flwr/client/mod/localdp_mod.py +6 -6
  58. flwr/client/mod/secure_aggregation/__init__.py +1 -1
  59. flwr/client/mod/secure_aggregation/secagg_mod.py +1 -1
  60. flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
  61. flwr/client/mod/utils.py +1 -1
  62. flwr/client/nodestate/__init__.py +1 -1
  63. flwr/client/nodestate/in_memory_nodestate.py +1 -1
  64. flwr/client/nodestate/nodestate.py +1 -1
  65. flwr/client/nodestate/nodestate_factory.py +1 -1
  66. flwr/client/numpy_client.py +1 -1
  67. flwr/client/rest_client/__init__.py +1 -1
  68. flwr/client/rest_client/connection.py +1 -1
  69. flwr/client/run_info_store.py +1 -1
  70. flwr/client/supernode/__init__.py +1 -1
  71. flwr/client/supernode/app.py +1 -1
  72. flwr/client/typing.py +1 -1
  73. flwr/common/__init__.py +1 -1
  74. flwr/common/address.py +1 -1
  75. flwr/common/args.py +1 -1
  76. flwr/common/auth_plugin/__init__.py +1 -1
  77. flwr/common/auth_plugin/auth_plugin.py +1 -1
  78. flwr/common/config.py +1 -1
  79. flwr/common/constant.py +1 -1
  80. flwr/common/context.py +1 -1
  81. flwr/common/date.py +1 -1
  82. flwr/common/differential_privacy.py +1 -1
  83. flwr/common/differential_privacy_constants.py +1 -1
  84. flwr/common/dp.py +1 -1
  85. flwr/common/exit/exit.py +6 -6
  86. flwr/common/exit_handlers.py +1 -1
  87. flwr/common/grpc.py +1 -1
  88. flwr/common/logger.py +1 -1
  89. flwr/common/message.py +28 -11
  90. flwr/common/object_ref.py +1 -1
  91. flwr/common/parameter.py +1 -1
  92. flwr/common/pyproject.py +1 -1
  93. flwr/common/record/__init__.py +1 -1
  94. flwr/common/record/arrayrecord.py +31 -31
  95. flwr/common/record/configrecord.py +13 -13
  96. flwr/common/record/conversion_utils.py +1 -1
  97. flwr/common/record/metricrecord.py +16 -16
  98. flwr/common/record/recorddict.py +138 -89
  99. flwr/common/record/typeddict.py +1 -1
  100. flwr/common/retry_invoker.py +10 -10
  101. flwr/common/secure_aggregation/__init__.py +1 -1
  102. flwr/common/secure_aggregation/crypto/__init__.py +1 -1
  103. flwr/common/secure_aggregation/crypto/shamir.py +52 -30
  104. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -1
  105. flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
  106. flwr/common/secure_aggregation/quantization.py +1 -1
  107. flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
  108. flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
  109. flwr/common/serde.py +1 -1
  110. flwr/common/telemetry.py +2 -2
  111. flwr/common/typing.py +1 -1
  112. flwr/common/version.py +1 -1
  113. flwr/proto/__init__.py +1 -1
  114. flwr/server/__init__.py +1 -1
  115. flwr/server/app.py +11 -11
  116. flwr/server/client_manager.py +1 -1
  117. flwr/server/client_proxy.py +1 -1
  118. flwr/server/compat/__init__.py +1 -1
  119. flwr/server/compat/app.py +1 -1
  120. flwr/server/compat/app_utils.py +1 -1
  121. flwr/server/compat/legacy_context.py +1 -1
  122. flwr/server/criterion.py +1 -1
  123. flwr/server/grid/grid.py +3 -3
  124. flwr/server/history.py +1 -1
  125. flwr/server/run_serverapp.py +1 -1
  126. flwr/server/server.py +1 -1
  127. flwr/server/server_app.py +65 -58
  128. flwr/server/server_config.py +1 -1
  129. flwr/server/serverapp/__init__.py +1 -1
  130. flwr/server/serverapp/app.py +1 -1
  131. flwr/server/serverapp_components.py +1 -1
  132. flwr/server/strategy/__init__.py +1 -1
  133. flwr/server/strategy/aggregate.py +1 -1
  134. flwr/server/strategy/bulyan.py +2 -2
  135. flwr/server/strategy/dp_adaptive_clipping.py +17 -17
  136. flwr/server/strategy/dp_fixed_clipping.py +17 -17
  137. flwr/server/strategy/dpfedavg_adaptive.py +1 -1
  138. flwr/server/strategy/dpfedavg_fixed.py +1 -1
  139. flwr/server/strategy/fault_tolerant_fedavg.py +1 -1
  140. flwr/server/strategy/fedadagrad.py +1 -1
  141. flwr/server/strategy/fedadam.py +1 -1
  142. flwr/server/strategy/fedavg.py +1 -1
  143. flwr/server/strategy/fedavg_android.py +1 -1
  144. flwr/server/strategy/fedavgm.py +1 -1
  145. flwr/server/strategy/fedmedian.py +1 -1
  146. flwr/server/strategy/fedopt.py +1 -1
  147. flwr/server/strategy/fedprox.py +1 -1
  148. flwr/server/strategy/fedtrimmedavg.py +1 -1
  149. flwr/server/strategy/fedxgb_bagging.py +1 -1
  150. flwr/server/strategy/fedxgb_cyclic.py +1 -1
  151. flwr/server/strategy/fedxgb_nn_avg.py +3 -2
  152. flwr/server/strategy/fedyogi.py +1 -1
  153. flwr/server/strategy/krum.py +1 -1
  154. flwr/server/strategy/qfedavg.py +1 -1
  155. flwr/server/strategy/strategy.py +1 -1
  156. flwr/server/superlink/__init__.py +1 -1
  157. flwr/server/superlink/ffs/__init__.py +1 -1
  158. flwr/server/superlink/ffs/disk_ffs.py +1 -1
  159. flwr/server/superlink/ffs/ffs.py +1 -1
  160. flwr/server/superlink/ffs/ffs_factory.py +1 -1
  161. flwr/server/superlink/fleet/__init__.py +1 -1
  162. flwr/server/superlink/fleet/grpc_adapter/__init__.py +1 -1
  163. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +1 -1
  164. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  165. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
  166. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
  167. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  168. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +13 -13
  169. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  170. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +1 -1
  171. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +1 -1
  172. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  173. flwr/server/superlink/fleet/message_handler/message_handler.py +1 -1
  174. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  175. flwr/server/superlink/fleet/rest_rere/rest_api.py +1 -1
  176. flwr/server/superlink/fleet/vce/__init__.py +1 -1
  177. flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
  178. flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
  179. flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
  180. flwr/server/superlink/fleet/vce/vce_api.py +1 -1
  181. flwr/server/superlink/linkstate/__init__.py +1 -1
  182. flwr/server/superlink/linkstate/in_memory_linkstate.py +1 -1
  183. flwr/server/superlink/linkstate/linkstate.py +1 -1
  184. flwr/server/superlink/linkstate/linkstate_factory.py +1 -1
  185. flwr/server/superlink/linkstate/sqlite_linkstate.py +1 -1
  186. flwr/server/superlink/linkstate/utils.py +1 -1
  187. flwr/server/superlink/simulation/__init__.py +1 -1
  188. flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
  189. flwr/server/superlink/simulation/simulationio_servicer.py +1 -1
  190. flwr/server/superlink/utils.py +1 -1
  191. flwr/server/typing.py +1 -1
  192. flwr/server/utils/__init__.py +1 -1
  193. flwr/server/utils/tensorboard.py +1 -1
  194. flwr/server/utils/validator.py +1 -1
  195. flwr/server/workflow/__init__.py +1 -1
  196. flwr/server/workflow/constant.py +1 -1
  197. flwr/server/workflow/default_workflows.py +1 -1
  198. flwr/server/workflow/secure_aggregation/__init__.py +1 -1
  199. flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -1
  200. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
  201. flwr/simulation/__init__.py +1 -1
  202. flwr/simulation/app.py +1 -1
  203. flwr/simulation/legacy_app.py +1 -1
  204. flwr/simulation/ray_transport/__init__.py +1 -1
  205. flwr/simulation/ray_transport/ray_actor.py +1 -1
  206. flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
  207. flwr/simulation/ray_transport/utils.py +1 -1
  208. flwr/simulation/run_simulation.py +2 -2
  209. flwr/simulation/simulationio_connection.py +1 -1
  210. flwr/superexec/__init__.py +1 -1
  211. flwr/superexec/app.py +1 -1
  212. flwr/superexec/deployment.py +1 -1
  213. flwr/superexec/exec_grpc.py +1 -1
  214. flwr/superexec/exec_servicer.py +1 -1
  215. flwr/superexec/exec_user_auth_interceptor.py +1 -1
  216. flwr/superexec/executor.py +1 -1
  217. flwr/superexec/simulation.py +1 -1
  218. {flwr-1.17.0.dist-info → flwr-1.18.0.dist-info}/METADATA +2 -2
  219. flwr-1.18.0.dist-info/RECORD +332 -0
  220. flwr-1.17.0.dist-info/LICENSE +0 -202
  221. flwr-1.17.0.dist-info/RECORD +0 -333
  222. {flwr-1.17.0.dist-info → flwr-1.18.0.dist-info}/WHEEL +0 -0
  223. {flwr-1.17.0.dist-info → flwr-1.18.0.dist-info}/entry_points.txt +0 -0
@@ -29,6 +29,22 @@ from .typeddict import TypedDict
29
29
 
30
30
  RecordType = Union[ArrayRecord, MetricRecord, ConfigRecord]
31
31
 
32
+
33
+ class _WarningTracker:
34
+ """A class to track warnings for deprecated properties."""
35
+
36
+ def __init__(self) -> None:
37
+ # These variables are used to ensure that the deprecation warnings
38
+ # for the deprecated properties/class are logged only once.
39
+ self.recordset_init_logged = False
40
+ self.recorddict_init_logged = False
41
+ self.parameters_records_logged = False
42
+ self.metrics_records_logged = False
43
+ self.configs_records_logged = False
44
+
45
+
46
+ _warning_tracker = _WarningTracker()
47
+
32
48
  T = TypeVar("T")
33
49
 
34
50
 
@@ -103,48 +119,74 @@ class RecordDict(TypedDict[str, RecordType]):
103
119
  are Python dictionaries designed to ensure that each key-value pair
104
120
  adheres to specified data types.
105
121
 
106
- Let's see an example.
107
-
108
- >>> from flwr.common import RecordDict
109
- >>> from flwr.common import ArrayRecord, ConfigRecord, MetricRecord
110
- >>>
111
- >>> # Let's begin with an empty record
112
- >>> my_records = RecordDict()
113
- >>>
114
- >>> # We can create a ConfigRecord
115
- >>> c_record = ConfigRecord({"lr": 0.1, "batch-size": 128})
116
- >>> # Adding it to the RecordDict would look like this
117
- >>> my_records["my_config"] = c_record
118
- >>>
119
- >>> # We can create a MetricRecord following a similar process
120
- >>> m_record = MetricRecord({"accuracy": 0.93, "losses": [0.23, 0.1]})
121
- >>> # Adding it to the RecordDict would look like this
122
- >>> my_records["my_metrics"] = m_record
122
+ Let's see an example::
123
+
124
+ from flwr.common import RecordDict
125
+ from flwr.common import ArrayRecord, ConfigRecord, MetricRecord
126
+
127
+ # Let's begin with an empty record
128
+ my_records = RecordDict()
129
+
130
+ # We can create a ConfigRecord
131
+ c_record = ConfigRecord({"lr": 0.1, "batch-size": 128})
132
+ # Adding it to the RecordDict would look like this
133
+ my_records["my_config"] = c_record
134
+
135
+ # We can create a MetricRecord following a similar process
136
+ m_record = MetricRecord({"accuracy": 0.93, "losses": [0.23, 0.1]})
137
+ # Adding it to the RecordDict would look like this
138
+ my_records["my_metrics"] = m_record
123
139
 
124
140
  Adding an :code:`ArrayRecord` follows the same steps as above but first,
125
141
  the array needs to be serialized and represented as a :code:`flwr.common.Array`.
126
- For example:
127
-
128
- >>> from flwr.common import Array
129
- >>> # Creating an ArrayRecord would look like this
130
- >>> arr_np = np.random.randn(3, 3)
131
- >>>
132
- >>> # You can use the built-in tool to serialize the array
133
- >>> arr = Array(arr_np)
134
- >>>
135
- >>> # Finally, create the record
136
- >>> arr_record = ArrayRecord({"my_array": arr})
137
- >>>
138
- >>> # Adding it to the RecordDict would look like this
139
- >>> my_records["my_parameters"] = arr_record
142
+ For example::
143
+
144
+ from flwr.common import Array
145
+ # Creating an ArrayRecord would look like this
146
+ arr_np = np.random.randn(3, 3)
147
+
148
+ # You can use the built-in tool to serialize the array
149
+ arr = Array(arr_np)
150
+
151
+ # Finally, create the record
152
+ arr_record = ArrayRecord({"my_array": arr})
153
+
154
+ # Adding it to the RecordDict would look like this
155
+ my_records["my_parameters"] = arr_record
140
156
 
141
157
  For additional examples on how to construct each of the records types shown
142
158
  above, please refer to the documentation for :code:`ConfigRecord`,
143
159
  :code:`MetricRecord` and :code:`ArrayRecord`.
144
160
  """
145
161
 
146
- def __init__(self, records: dict[str, RecordType] | None = None) -> None:
162
+ def __init__(
163
+ self,
164
+ records: dict[str, RecordType] | None = None,
165
+ *,
166
+ parameters_records: dict[str, ArrayRecord] | None = None,
167
+ metrics_records: dict[str, MetricRecord] | None = None,
168
+ configs_records: dict[str, ConfigRecord] | None = None,
169
+ ) -> None:
147
170
  super().__init__(_check_key, _check_value)
171
+
172
+ # Warning for deprecated usage
173
+ if (
174
+ parameters_records is not None
175
+ or metrics_records is not None
176
+ or configs_records is not None
177
+ ):
178
+ log(
179
+ WARN,
180
+ "The arguments `parameters_records`, `metrics_records`, and "
181
+ "`configs_records` of `RecordDict` are deprecated and will "
182
+ "be removed in a future release. "
183
+ "Please pass all records using the `records` argument instead.",
184
+ )
185
+ records = records or {}
186
+ records.update(parameters_records or {})
187
+ records.update(metrics_records or {})
188
+ records.update(configs_records or {})
189
+
148
190
  if records is not None:
149
191
  for key, record in records.items():
150
192
  self[key] = record
@@ -196,6 +238,54 @@ class RecordDict(TypedDict[str, RecordType]):
196
238
  type(value).__name__,
197
239
  )
198
240
 
241
+ @property
242
+ def parameters_records(self) -> TypedDict[str, ArrayRecord]:
243
+ """Deprecated property.
244
+
245
+ Use ``array_records`` instead.
246
+ """
247
+ if _warning_tracker.parameters_records_logged:
248
+ _warning_tracker.parameters_records_logged = True
249
+ log(
250
+ WARN,
251
+ "The `parameters_records` property of `RecordDict` "
252
+ "(formerly `RecordSet`) is deprecated and will be removed in a "
253
+ "future release. Please use the `array_records` property instead.",
254
+ )
255
+ return self.array_records
256
+
257
+ @property
258
+ def metrics_records(self) -> TypedDict[str, MetricRecord]:
259
+ """Deprecated property.
260
+
261
+ Use ``metric_records`` instead.
262
+ """
263
+ if not _warning_tracker.metrics_records_logged:
264
+ _warning_tracker.metrics_records_logged = True
265
+ log(
266
+ WARN,
267
+ "The `metrics_records` property of `RecordDict` "
268
+ "(formerly `RecordSet`) is deprecated and will be removed in a "
269
+ "future release. Please use the `metric_records` property instead.",
270
+ )
271
+ return self.metric_records
272
+
273
+ @property
274
+ def configs_records(self) -> TypedDict[str, ConfigRecord]:
275
+ """Deprecated property.
276
+
277
+ Use ``config_records`` instead.
278
+ """
279
+ if not _warning_tracker.configs_records_logged:
280
+ _warning_tracker.configs_records_logged = True
281
+ log(
282
+ WARN,
283
+ "The `configs_records` property of `RecordDict` "
284
+ "(formerly `RecordSet`) is deprecated and will be removed in a "
285
+ "future release. Please use the `config_records` property instead.",
286
+ )
287
+ return self.config_records
288
+
199
289
 
200
290
  class RecordSet(RecordDict):
201
291
  """Deprecated class ``RecordSet``, use ``RecordDict`` instead.
@@ -223,66 +313,25 @@ class RecordSet(RecordDict):
223
313
  my_content = RecordDict()
224
314
  """
225
315
 
226
- _warning_logged = False
227
- _warning_logged_params = False
228
- _warning_logged_metrics = False
229
- _warning_logged_configs = False
230
-
231
- def __init__(self, records: dict[str, RecordType] | None = None) -> None:
232
- if not RecordSet._warning_logged:
233
- RecordSet._warning_logged = True
316
+ def __init__(
317
+ self,
318
+ records: dict[str, RecordType] | None = None,
319
+ *,
320
+ parameters_records: dict[str, ArrayRecord] | None = None,
321
+ metrics_records: dict[str, MetricRecord] | None = None,
322
+ configs_records: dict[str, ConfigRecord] | None = None,
323
+ ) -> None:
324
+ if not _warning_tracker.recordset_init_logged:
325
+ _warning_tracker.recordset_init_logged = True
234
326
  log(
235
327
  WARN,
236
328
  "The `RecordSet` class has been renamed to `RecordDict`. "
237
329
  "Support for `RecordSet` will be removed in a future release. "
238
330
  "Please update your code accordingly.",
239
331
  )
240
- super().__init__(records)
241
-
242
- @property
243
- def parameters_records(self) -> TypedDict[str, ArrayRecord]:
244
- """Deprecated property.
245
-
246
- Use ``array_records`` instead.
247
- """
248
- if not RecordSet._warning_logged_params:
249
- RecordSet._warning_logged_params = True
250
- log(
251
- WARN,
252
- "`RecordSet.parameters_records` has been deprecated "
253
- "and will be removed in a future release. Please use "
254
- "`RecordDict.array_records` instead.",
255
- )
256
- return self.array_records
257
-
258
- @property
259
- def metrics_records(self) -> TypedDict[str, MetricRecord]:
260
- """Deprecated property.
261
-
262
- Use ``metric_records`` instead.
263
- """
264
- if not RecordSet._warning_logged_metrics:
265
- RecordSet._warning_logged_metrics = True
266
- log(
267
- WARN,
268
- "`RecordSet.metrics_records` has been deprecated "
269
- "and will be removed in a future release. Please use "
270
- "`RecordDict.metric_records` instead.",
271
- )
272
- return self.metric_records
273
-
274
- @property
275
- def configs_records(self) -> TypedDict[str, ConfigRecord]:
276
- """Deprecated property.
277
-
278
- Use ``config_records`` instead.
279
- """
280
- if not RecordSet._warning_logged_configs:
281
- RecordSet._warning_logged_configs = True
282
- log(
283
- WARN,
284
- "`RecordSet.configs_records` has been deprecated "
285
- "and will be removed in a future release. Please use "
286
- "`RecordDict.config_records` instead.",
287
- )
288
- return self.config_records
332
+ super().__init__(
333
+ records,
334
+ parameters_records=parameters_records,
335
+ metrics_records=metrics_records,
336
+ configs_records=configs_records,
337
+ )
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -166,15 +166,15 @@ class RetryInvoker:
166
166
 
167
167
  Examples
168
168
  --------
169
- Initialize a `RetryInvoker` with exponential backoff and invoke a function:
170
-
171
- >>> invoker = RetryInvoker(
172
- ... exponential, # Or use `lambda: exponential(3, 2)` to pass arguments
173
- ... grpc.RpcError,
174
- ... max_tries=3,
175
- ... max_time=None,
176
- ... )
177
- >>> invoker.invoke(my_func, arg1, arg2, kw1=kwarg1)
169
+ Initialize a `RetryInvoker` with exponential backoff and invoke a function::
170
+
171
+ invoker = RetryInvoker(
172
+ exponential, # Or use `lambda: exponential(3, 2)` to pass arguments
173
+ grpc.RpcError,
174
+ max_tries=3,
175
+ max_time=None,
176
+ )
177
+ invoker.invoke(my_func, arg1, arg2, kw1=kwarg1)
178
178
  """
179
179
 
180
180
  # pylint: disable-next=too-many-arguments
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -15,61 +15,83 @@
15
15
  """Shamir's secret sharing."""
16
16
 
17
17
 
18
- import pickle
18
+ import os
19
19
  from concurrent.futures import ThreadPoolExecutor
20
- from typing import cast
21
20
 
22
21
  from Crypto.Protocol.SecretSharing import Shamir
23
22
  from Crypto.Util.Padding import pad, unpad
24
23
 
25
24
 
26
25
  def create_shares(secret: bytes, threshold: int, num: int) -> list[bytes]:
27
- """Return list of shares (bytes)."""
26
+ """Return a list of shares (bytes).
27
+
28
+ Shares are created from the provided secret using Shamir's secret sharing.
29
+ """
30
+ # Shamir's secret sharing requires the secret to be a multiple of 16 bytes
31
+ # (AES block size). Pad the secret to the next multiple of 16 bytes.
28
32
  secret_padded = pad(secret, 16)
29
- secret_padded_chunk = [
30
- (threshold, num, secret_padded[i : i + 16])
31
- for i in range(0, len(secret_padded), 16)
32
- ]
33
- share_list: list[list[tuple[int, bytes]]] = [[] for _ in range(num)]
33
+ chunks = [secret_padded[i : i + 16] for i in range(0, len(secret_padded), 16)]
34
+
35
+ # The share list should contain shares of the secret, and each share consists of:
36
+ # <4 bytes of index><share of chunk1><share of chunk2>...<share of chunkN>
37
+ share_list: list[bytearray] = [bytearray() for _ in range(num)]
34
38
 
35
- with ThreadPoolExecutor(max_workers=10) as executor:
39
+ # Create shares for each chunk in parallel
40
+ max_workers = min(len(chunks), os.cpu_count() or 1)
41
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
36
42
  for chunk_shares in executor.map(
37
- lambda arg: _shamir_split(*arg), secret_padded_chunk
43
+ lambda chunk: _shamir_split(threshold, num, chunk), chunks
38
44
  ):
39
45
  for idx, share in chunk_shares:
40
- # Index in `chunk_shares` starts from 1
41
- share_list[idx - 1].append((idx, share))
46
+ # Initialize the share with the index if it is empty
47
+ if not share_list[idx - 1]:
48
+ share_list[idx - 1] += idx.to_bytes(4, "little", signed=False)
42
49
 
43
- return [pickle.dumps(shares) for shares in share_list]
50
+ # Append the share to the bytes
51
+ share_list[idx - 1] += share
52
+
53
+ return [bytes(share) for share in share_list]
44
54
 
45
55
 
46
56
  def _shamir_split(threshold: int, num: int, chunk: bytes) -> list[tuple[int, bytes]]:
57
+ """Create shares for a chunk using Shamir's secret sharing.
58
+
59
+ Each share is a tuple (index, share_bytes), where share_bytes is 16 bytes long.
60
+ """
47
61
  return Shamir.split(threshold, num, chunk, ssss=False)
48
62
 
49
63
 
50
- # Reconstructing secret with PyCryptodome
51
64
  def combine_shares(share_list: list[bytes]) -> bytes:
52
- """Reconstruct secret from shares."""
53
- unpickled_share_list: list[list[tuple[int, bytes]]] = [
54
- cast(list[tuple[int, bytes]], pickle.loads(share)) for share in share_list
55
- ]
65
+ """Reconstruct the secret from a list of shares."""
66
+ # Compute the number of chunks
67
+ # Each share contains 4 bytes of index and 16 bytes of share for each chunk
68
+ chunk_num = (len(share_list[0]) - 4) >> 4
56
69
 
57
- chunk_num = len(unpickled_share_list[0])
58
70
  secret_padded = bytearray(0)
59
- chunk_shares_list: list[list[tuple[int, bytes]]] = []
60
- for i in range(chunk_num):
61
- chunk_shares: list[tuple[int, bytes]] = []
62
- for share in unpickled_share_list:
63
- chunk_shares.append(share[i])
64
- chunk_shares_list.append(chunk_shares)
65
-
66
- with ThreadPoolExecutor(max_workers=10) as executor:
71
+ chunk_shares_list: list[list[tuple[int, bytes]]] = [[] for _ in range(chunk_num)]
72
+
73
+ # Split shares into chunks
74
+ for share in share_list:
75
+ # The first 4 bytes are the index
76
+ index = int.from_bytes(share[:4], "little", signed=False)
77
+ for i in range(chunk_num):
78
+ start = (i << 4) + 4
79
+ chunk_shares_list[i].append((index, share[start : start + 16]))
80
+
81
+ # Combine shares for each chunk in parallel
82
+ max_workers = min(chunk_num, os.cpu_count() or 1)
83
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
67
84
  for chunk in executor.map(_shamir_combine, chunk_shares_list):
68
85
  secret_padded += chunk
69
86
 
70
- secret = unpad(secret_padded, 16)
71
- return bytes(secret)
87
+ try:
88
+ secret = unpad(bytes(secret_padded), 16)
89
+ except ValueError:
90
+ # If unpadding fails, it means the shares are not valid
91
+ raise ValueError("Failed to combine shares") from None
92
+ return secret
72
93
 
73
94
 
74
95
  def _shamir_combine(shares: list[tuple[int, bytes]]) -> bytes:
96
+ """Reconstruct a chunk from shares using Shamir's secret sharing."""
75
97
  return Shamir.combine(shares, ssss=False)
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
flwr/common/serde.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
flwr/common/telemetry.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -124,7 +124,7 @@ class EventType(str, Enum):
124
124
  # This method combined with auto() will set the property value to
125
125
  # the property name e.g.
126
126
  # `START_CLIENT = auto()` becomes `START_CLIENT = "START_CLIENT"`
127
- # The type signature is not compatible with mypy, pylint and flake8
127
+ # The type signature is not compatible with mypy and pylint
128
128
  # so each of those needs to be disabled for this line.
129
129
  # pylint: disable-next=no-self-argument,arguments-differ,line-too-long
130
130
  def _generate_next_value_(name: str, start: int, count: int, last_values: list[Any]) -> Any: # type: ignore # noqa: E501
flwr/common/typing.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
flwr/common/version.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
flwr/proto/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
flwr/server/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
flwr/server/app.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -183,19 +183,19 @@ def start_server( # pylint: disable=too-many-arguments,too-many-locals
183
183
 
184
184
  Examples
185
185
  --------
186
- Starting an insecure server:
186
+ Starting an insecure server::
187
187
 
188
- >>> start_server()
188
+ start_server()
189
189
 
190
- Starting an SSL-enabled server:
190
+ Starting a TLS-enabled server::
191
191
 
192
- >>> start_server(
193
- >>> certificates=(
194
- >>> Path("/crts/root.pem").read_bytes(),
195
- >>> Path("/crts/localhost.crt").read_bytes(),
196
- >>> Path("/crts/localhost.key").read_bytes()
197
- >>> )
198
- >>> )
192
+ start_server(
193
+ certificates=(
194
+ Path("/crts/root.pem").read_bytes(),
195
+ Path("/crts/localhost.crt").read_bytes(),
196
+ Path("/crts/localhost.key").read_bytes()
197
+ )
198
+ )
199
199
  """
200
200
  msg = (
201
201
  "flwr.server.start_server() is deprecated."
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
flwr/server/compat/app.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
flwr/server/criterion.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
flwr/server/grid/grid.py CHANGED
@@ -28,11 +28,11 @@ class Grid(ABC):
28
28
 
29
29
  @abstractmethod
30
30
  def set_run(self, run_id: int) -> None:
31
- """Request a run to the SuperLink with a given `run_id`.
31
+ """Request a run to the SuperLink with a given ``run_id``.
32
32
 
33
33
  If a ``Run`` with the specified ``run_id`` exists, a local ``Run``
34
34
  object will be created. It enables further functionality
35
- in the grid, such as sending ``Message``s.
35
+ in the grid, such as sending ``Message`` objects.
36
36
 
37
37
  Parameters
38
38
  ----------
@@ -172,7 +172,7 @@ class Driver(Grid):
172
172
 
173
173
  .. warning::
174
174
  ``Driver`` is deprecated and will be removed in a future release.
175
- Use `Grid` in the signature of your ServerApp.
175
+ Use ``Grid`` in the signature of your ServerApp.
176
176
 
177
177
  Examples
178
178
  --------
flwr/server/history.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.