flwr 1.17.0__py3-none-any.whl → 1.19.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 (286) hide show
  1. flwr/__init__.py +1 -1
  2. flwr/app/__init__.py +15 -0
  3. flwr/app/error.py +68 -0
  4. flwr/app/metadata.py +223 -0
  5. flwr/cli/__init__.py +1 -1
  6. flwr/cli/app.py +21 -2
  7. flwr/cli/build.py +83 -58
  8. flwr/cli/cli_user_auth_interceptor.py +1 -1
  9. flwr/cli/config_utils.py +53 -17
  10. flwr/cli/example.py +1 -1
  11. flwr/cli/install.py +1 -1
  12. flwr/cli/log.py +4 -4
  13. flwr/cli/login/__init__.py +1 -1
  14. flwr/cli/login/login.py +15 -8
  15. flwr/cli/ls.py +16 -37
  16. flwr/cli/new/__init__.py +1 -1
  17. flwr/cli/new/new.py +4 -4
  18. flwr/cli/new/templates/__init__.py +1 -1
  19. flwr/cli/new/templates/app/__init__.py +1 -1
  20. flwr/cli/new/templates/app/code/__init__.py +1 -1
  21. flwr/cli/new/templates/app/code/client.baseline.py.tpl +1 -1
  22. flwr/cli/new/templates/app/code/flwr_tune/__init__.py +1 -1
  23. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +4 -4
  24. flwr/cli/new/templates/app/code/model.baseline.py.tpl +1 -1
  25. flwr/cli/new/templates/app/code/server.baseline.py.tpl +2 -3
  26. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +1 -1
  27. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -17
  28. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
  29. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
  30. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
  31. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
  32. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
  33. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
  34. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
  35. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
  36. flwr/cli/run/__init__.py +1 -1
  37. flwr/cli/run/run.py +11 -19
  38. flwr/cli/stop.py +3 -3
  39. flwr/cli/utils.py +42 -17
  40. flwr/client/__init__.py +3 -3
  41. flwr/client/client.py +1 -1
  42. flwr/client/client_app.py +140 -138
  43. flwr/client/clientapp/__init__.py +1 -8
  44. flwr/client/clientapp/utils.py +1 -1
  45. flwr/client/dpfedavg_numpy_client.py +1 -1
  46. flwr/client/grpc_adapter_client/__init__.py +1 -1
  47. flwr/client/grpc_adapter_client/connection.py +5 -5
  48. flwr/client/grpc_rere_client/__init__.py +1 -1
  49. flwr/client/grpc_rere_client/client_interceptor.py +1 -1
  50. flwr/client/grpc_rere_client/connection.py +131 -61
  51. flwr/client/grpc_rere_client/grpc_adapter.py +35 -7
  52. flwr/client/message_handler/__init__.py +1 -1
  53. flwr/client/message_handler/message_handler.py +2 -2
  54. flwr/client/mod/__init__.py +1 -1
  55. flwr/client/mod/centraldp_mods.py +1 -1
  56. flwr/client/mod/comms_mods.py +39 -20
  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/numpy_client.py +1 -1
  63. flwr/client/rest_client/__init__.py +1 -1
  64. flwr/client/rest_client/connection.py +174 -68
  65. flwr/client/run_info_store.py +1 -1
  66. flwr/client/typing.py +1 -1
  67. flwr/clientapp/__init__.py +15 -0
  68. flwr/common/__init__.py +3 -3
  69. flwr/common/address.py +1 -1
  70. flwr/common/args.py +1 -1
  71. flwr/common/auth_plugin/__init__.py +3 -1
  72. flwr/common/auth_plugin/auth_plugin.py +30 -4
  73. flwr/common/config.py +1 -1
  74. flwr/common/constant.py +37 -8
  75. flwr/common/context.py +1 -1
  76. flwr/common/date.py +1 -1
  77. flwr/common/differential_privacy.py +1 -1
  78. flwr/common/differential_privacy_constants.py +1 -1
  79. flwr/common/dp.py +1 -1
  80. flwr/common/event_log_plugin/event_log_plugin.py +3 -3
  81. flwr/common/exit/exit.py +6 -6
  82. flwr/common/exit_handlers.py +31 -1
  83. flwr/common/grpc.py +1 -1
  84. flwr/common/heartbeat.py +165 -0
  85. flwr/common/inflatable.py +290 -0
  86. flwr/common/inflatable_grpc_utils.py +99 -0
  87. flwr/common/inflatable_rest_utils.py +99 -0
  88. flwr/common/inflatable_utils.py +341 -0
  89. flwr/common/logger.py +1 -1
  90. flwr/common/message.py +137 -252
  91. flwr/common/object_ref.py +1 -1
  92. flwr/common/parameter.py +1 -1
  93. flwr/common/pyproject.py +1 -1
  94. flwr/common/record/__init__.py +3 -2
  95. flwr/common/record/array.py +323 -0
  96. flwr/common/record/arrayrecord.py +121 -243
  97. flwr/common/record/configrecord.py +71 -16
  98. flwr/common/record/conversion_utils.py +2 -2
  99. flwr/common/record/metricrecord.py +71 -20
  100. flwr/common/record/recorddict.py +207 -90
  101. flwr/common/record/typeddict.py +1 -1
  102. flwr/common/recorddict_compat.py +2 -2
  103. flwr/common/retry_invoker.py +15 -11
  104. flwr/common/secure_aggregation/__init__.py +1 -1
  105. flwr/common/secure_aggregation/crypto/__init__.py +1 -1
  106. flwr/common/secure_aggregation/crypto/shamir.py +52 -30
  107. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -1
  108. flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
  109. flwr/common/secure_aggregation/quantization.py +1 -1
  110. flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
  111. flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
  112. flwr/common/serde.py +60 -184
  113. flwr/common/serde_utils.py +175 -0
  114. flwr/common/telemetry.py +2 -2
  115. flwr/common/typing.py +6 -4
  116. flwr/common/version.py +1 -1
  117. flwr/compat/__init__.py +15 -0
  118. flwr/compat/client/__init__.py +15 -0
  119. flwr/{client → compat/client}/app.py +71 -211
  120. flwr/{client → compat/client}/grpc_client/__init__.py +1 -1
  121. flwr/{client → compat/client}/grpc_client/connection.py +13 -13
  122. flwr/compat/common/__init__.py +15 -0
  123. flwr/compat/server/__init__.py +15 -0
  124. flwr/compat/server/app.py +174 -0
  125. flwr/compat/simulation/__init__.py +15 -0
  126. flwr/proto/__init__.py +1 -1
  127. flwr/proto/fleet_pb2.py +32 -27
  128. flwr/proto/fleet_pb2.pyi +49 -35
  129. flwr/proto/fleet_pb2_grpc.py +117 -13
  130. flwr/proto/fleet_pb2_grpc.pyi +47 -6
  131. flwr/proto/heartbeat_pb2.py +33 -0
  132. flwr/proto/heartbeat_pb2.pyi +66 -0
  133. flwr/proto/heartbeat_pb2_grpc.py +4 -0
  134. flwr/proto/heartbeat_pb2_grpc.pyi +4 -0
  135. flwr/proto/message_pb2.py +28 -11
  136. flwr/proto/message_pb2.pyi +125 -0
  137. flwr/proto/recorddict_pb2.py +16 -28
  138. flwr/proto/recorddict_pb2.pyi +46 -64
  139. flwr/proto/run_pb2.py +24 -32
  140. flwr/proto/run_pb2.pyi +4 -52
  141. flwr/proto/serverappio_pb2.py +32 -23
  142. flwr/proto/serverappio_pb2.pyi +45 -3
  143. flwr/proto/serverappio_pb2_grpc.py +138 -34
  144. flwr/proto/serverappio_pb2_grpc.pyi +54 -13
  145. flwr/proto/simulationio_pb2.py +12 -11
  146. flwr/proto/simulationio_pb2_grpc.py +35 -0
  147. flwr/proto/simulationio_pb2_grpc.pyi +14 -0
  148. flwr/server/__init__.py +2 -2
  149. flwr/server/app.py +69 -187
  150. flwr/server/client_manager.py +1 -1
  151. flwr/server/client_proxy.py +1 -1
  152. flwr/server/compat/__init__.py +1 -1
  153. flwr/server/compat/app.py +1 -1
  154. flwr/server/compat/app_utils.py +51 -29
  155. flwr/server/compat/legacy_context.py +1 -1
  156. flwr/server/criterion.py +1 -1
  157. flwr/server/fleet_event_log_interceptor.py +2 -2
  158. flwr/server/grid/grid.py +3 -3
  159. flwr/server/grid/grpc_grid.py +104 -34
  160. flwr/server/grid/inmemory_grid.py +5 -4
  161. flwr/server/history.py +1 -1
  162. flwr/server/run_serverapp.py +1 -1
  163. flwr/server/server.py +1 -1
  164. flwr/server/server_app.py +65 -58
  165. flwr/server/server_config.py +1 -1
  166. flwr/server/serverapp/__init__.py +1 -1
  167. flwr/server/serverapp/app.py +19 -1
  168. flwr/server/serverapp_components.py +1 -1
  169. flwr/server/strategy/__init__.py +1 -1
  170. flwr/server/strategy/aggregate.py +1 -1
  171. flwr/server/strategy/bulyan.py +2 -2
  172. flwr/server/strategy/dp_adaptive_clipping.py +17 -17
  173. flwr/server/strategy/dp_fixed_clipping.py +17 -17
  174. flwr/server/strategy/dpfedavg_adaptive.py +1 -1
  175. flwr/server/strategy/dpfedavg_fixed.py +1 -1
  176. flwr/server/strategy/fault_tolerant_fedavg.py +1 -1
  177. flwr/server/strategy/fedadagrad.py +1 -1
  178. flwr/server/strategy/fedadam.py +1 -1
  179. flwr/server/strategy/fedavg.py +1 -1
  180. flwr/server/strategy/fedavg_android.py +1 -1
  181. flwr/server/strategy/fedavgm.py +1 -1
  182. flwr/server/strategy/fedmedian.py +1 -1
  183. flwr/server/strategy/fedopt.py +1 -1
  184. flwr/server/strategy/fedprox.py +1 -1
  185. flwr/server/strategy/fedtrimmedavg.py +1 -1
  186. flwr/server/strategy/fedxgb_bagging.py +1 -1
  187. flwr/server/strategy/fedxgb_cyclic.py +1 -1
  188. flwr/server/strategy/fedxgb_nn_avg.py +3 -2
  189. flwr/server/strategy/fedyogi.py +1 -1
  190. flwr/server/strategy/krum.py +1 -1
  191. flwr/server/strategy/qfedavg.py +1 -1
  192. flwr/server/strategy/strategy.py +1 -1
  193. flwr/server/superlink/__init__.py +1 -1
  194. flwr/server/superlink/ffs/__init__.py +3 -1
  195. flwr/server/superlink/ffs/disk_ffs.py +1 -1
  196. flwr/server/superlink/ffs/ffs.py +1 -1
  197. flwr/server/superlink/ffs/ffs_factory.py +1 -1
  198. flwr/server/superlink/fleet/__init__.py +1 -1
  199. flwr/server/superlink/fleet/grpc_adapter/__init__.py +1 -1
  200. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +14 -4
  201. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  202. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
  203. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
  204. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  205. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +13 -13
  206. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  207. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +102 -8
  208. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +1 -1
  209. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  210. flwr/server/superlink/fleet/message_handler/message_handler.py +136 -19
  211. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  212. flwr/server/superlink/fleet/rest_rere/rest_api.py +73 -12
  213. flwr/server/superlink/fleet/vce/__init__.py +1 -1
  214. flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
  215. flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
  216. flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
  217. flwr/server/superlink/fleet/vce/vce_api.py +7 -4
  218. flwr/server/superlink/linkstate/__init__.py +1 -1
  219. flwr/server/superlink/linkstate/in_memory_linkstate.py +139 -44
  220. flwr/server/superlink/linkstate/linkstate.py +54 -21
  221. flwr/server/superlink/linkstate/linkstate_factory.py +1 -1
  222. flwr/server/superlink/linkstate/sqlite_linkstate.py +150 -56
  223. flwr/server/superlink/linkstate/utils.py +34 -30
  224. flwr/server/superlink/serverappio/serverappio_grpc.py +3 -0
  225. flwr/server/superlink/serverappio/serverappio_servicer.py +211 -57
  226. flwr/server/superlink/simulation/__init__.py +1 -1
  227. flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
  228. flwr/server/superlink/simulation/simulationio_servicer.py +26 -2
  229. flwr/server/superlink/utils.py +45 -3
  230. flwr/server/typing.py +1 -1
  231. flwr/server/utils/__init__.py +1 -1
  232. flwr/server/utils/tensorboard.py +1 -1
  233. flwr/server/utils/validator.py +3 -3
  234. flwr/server/workflow/__init__.py +1 -1
  235. flwr/server/workflow/constant.py +1 -1
  236. flwr/server/workflow/default_workflows.py +1 -1
  237. flwr/server/workflow/secure_aggregation/__init__.py +1 -1
  238. flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -1
  239. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
  240. flwr/serverapp/__init__.py +15 -0
  241. flwr/simulation/__init__.py +1 -1
  242. flwr/simulation/app.py +18 -1
  243. flwr/simulation/legacy_app.py +1 -1
  244. flwr/simulation/ray_transport/__init__.py +1 -1
  245. flwr/simulation/ray_transport/ray_actor.py +1 -1
  246. flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
  247. flwr/simulation/ray_transport/utils.py +1 -1
  248. flwr/simulation/run_simulation.py +2 -2
  249. flwr/simulation/simulationio_connection.py +1 -1
  250. flwr/supercore/__init__.py +15 -0
  251. flwr/supercore/object_store/__init__.py +24 -0
  252. flwr/supercore/object_store/in_memory_object_store.py +229 -0
  253. flwr/supercore/object_store/object_store.py +192 -0
  254. flwr/supercore/object_store/object_store_factory.py +44 -0
  255. flwr/superexec/__init__.py +1 -1
  256. flwr/superexec/app.py +1 -1
  257. flwr/superexec/deployment.py +7 -3
  258. flwr/superexec/exec_event_log_interceptor.py +4 -4
  259. flwr/superexec/exec_grpc.py +8 -4
  260. flwr/superexec/exec_servicer.py +126 -24
  261. flwr/superexec/exec_user_auth_interceptor.py +38 -9
  262. flwr/superexec/executor.py +5 -1
  263. flwr/superexec/simulation.py +8 -2
  264. flwr/superlink/__init__.py +15 -0
  265. flwr/{client/supernode → supernode}/__init__.py +1 -8
  266. flwr/{client/nodestate/nodestate.py → supernode/cli/__init__.py} +8 -15
  267. flwr/{client/supernode/app.py → supernode/cli/flower_supernode.py} +4 -13
  268. flwr/supernode/cli/flwr_clientapp.py +81 -0
  269. flwr/{client → supernode}/nodestate/__init__.py +1 -1
  270. flwr/supernode/nodestate/in_memory_nodestate.py +190 -0
  271. flwr/supernode/nodestate/nodestate.py +212 -0
  272. flwr/{client → supernode}/nodestate/nodestate_factory.py +1 -1
  273. flwr/supernode/runtime/__init__.py +15 -0
  274. flwr/{client/clientapp/app.py → supernode/runtime/run_clientapp.py} +26 -57
  275. flwr/supernode/servicer/__init__.py +15 -0
  276. flwr/supernode/servicer/clientappio/__init__.py +24 -0
  277. flwr/{client/clientapp → supernode/servicer/clientappio}/clientappio_servicer.py +1 -1
  278. flwr/supernode/start_client_internal.py +491 -0
  279. {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/METADATA +6 -5
  280. flwr-1.19.0.dist-info/RECORD +365 -0
  281. {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/WHEEL +1 -1
  282. {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/entry_points.txt +2 -2
  283. flwr/client/heartbeat.py +0 -74
  284. flwr/client/nodestate/in_memory_nodestate.py +0 -38
  285. flwr-1.17.0.dist-info/LICENSE +0 -202
  286. flwr-1.17.0.dist-info/RECORD +0 -333
@@ -0,0 +1,175 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Utils for serde."""
16
+
17
+ from collections.abc import MutableMapping
18
+ from typing import Any, TypeVar, cast
19
+
20
+ from google.protobuf.message import Message as GrpcMessage
21
+
22
+ # pylint: disable=E0611
23
+ from flwr.proto.error_pb2 import Error as ProtoError
24
+ from flwr.proto.message_pb2 import Metadata as ProtoMetadata
25
+ from flwr.proto.recorddict_pb2 import (
26
+ BoolList,
27
+ BytesList,
28
+ DoubleList,
29
+ SintList,
30
+ StringList,
31
+ UintList,
32
+ )
33
+
34
+ from ..app.error import Error
35
+ from ..app.metadata import Metadata
36
+ from .constant import INT64_MAX_VALUE
37
+ from .record.typeddict import TypedDict
38
+
39
+ # pylint: enable=E0611
40
+
41
+ _type_to_field: dict[type, str] = {
42
+ float: "double",
43
+ int: "sint64",
44
+ bool: "bool",
45
+ str: "string",
46
+ bytes: "bytes",
47
+ }
48
+ _list_type_to_class_and_field: dict[type, tuple[type[GrpcMessage], str]] = {
49
+ float: (DoubleList, "double_list"),
50
+ int: (SintList, "sint_list"),
51
+ bool: (BoolList, "bool_list"),
52
+ str: (StringList, "string_list"),
53
+ bytes: (BytesList, "bytes_list"),
54
+ }
55
+ T = TypeVar("T")
56
+
57
+
58
+ def _is_uint64(value: Any) -> bool:
59
+ """Check if a value is uint64."""
60
+ return isinstance(value, int) and value > INT64_MAX_VALUE
61
+
62
+
63
+ def _record_value_to_proto(
64
+ value: Any, allowed_types: list[type], proto_class: type[T]
65
+ ) -> T:
66
+ """Serialize `*RecordValue` to ProtoBuf.
67
+
68
+ Note: `bool` MUST be put in the front of allowd_types if it exists.
69
+ """
70
+ arg = {}
71
+ for t in allowed_types:
72
+ # Single element
73
+ # Note: `isinstance(False, int) == True`.
74
+ if isinstance(value, t):
75
+ fld = _type_to_field[t]
76
+ if t is int and _is_uint64(value):
77
+ fld = "uint64"
78
+ arg[fld] = value
79
+ return proto_class(**arg)
80
+ # List
81
+ if isinstance(value, list) and all(isinstance(item, t) for item in value):
82
+ list_class, fld = _list_type_to_class_and_field[t]
83
+ # Use UintList if any element is of type `uint64`.
84
+ if t is int and any(_is_uint64(v) for v in value):
85
+ list_class, fld = UintList, "uint_list"
86
+ arg[fld] = list_class(vals=value)
87
+ return proto_class(**arg)
88
+ # Invalid types
89
+ raise TypeError(
90
+ f"The type of the following value is not allowed "
91
+ f"in '{proto_class.__name__}':\n{value}"
92
+ )
93
+
94
+
95
+ def _record_value_from_proto(value_proto: GrpcMessage) -> Any:
96
+ """Deserialize `*RecordValue` from ProtoBuf."""
97
+ value_field = cast(str, value_proto.WhichOneof("value"))
98
+ if value_field.endswith("list"):
99
+ value = list(getattr(value_proto, value_field).vals)
100
+ else:
101
+ value = getattr(value_proto, value_field)
102
+ return value
103
+
104
+
105
+ def record_value_dict_to_proto(
106
+ value_dict: TypedDict[str, Any],
107
+ allowed_types: list[type],
108
+ value_proto_class: type[T],
109
+ ) -> dict[str, T]:
110
+ """Serialize the record value dict to ProtoBuf.
111
+
112
+ This function will preserve the order of the keys in the input dictionary.
113
+
114
+ Note: `bool` MUST be put in the front of allowd_types if it exists.
115
+ """
116
+ # Move bool to the front
117
+ if bool in allowed_types and allowed_types[0] != bool:
118
+ allowed_types.remove(bool)
119
+ allowed_types.insert(0, bool)
120
+
121
+ def proto(_v: Any) -> T:
122
+ return _record_value_to_proto(_v, allowed_types, value_proto_class)
123
+
124
+ return {k: proto(v) for k, v in value_dict.items()}
125
+
126
+
127
+ def record_value_dict_from_proto(
128
+ value_dict_proto: MutableMapping[str, Any]
129
+ ) -> dict[str, Any]:
130
+ """Deserialize the record value dict from ProtoBuf."""
131
+ return {k: _record_value_from_proto(v) for k, v in value_dict_proto.items()}
132
+
133
+
134
+ def error_to_proto(error: Error) -> ProtoError:
135
+ """Serialize Error to ProtoBuf."""
136
+ reason = error.reason if error.reason else ""
137
+ return ProtoError(code=error.code, reason=reason)
138
+
139
+
140
+ def error_from_proto(error_proto: ProtoError) -> Error:
141
+ """Deserialize Error from ProtoBuf."""
142
+ reason = error_proto.reason if len(error_proto.reason) > 0 else None
143
+ return Error(code=error_proto.code, reason=reason)
144
+
145
+
146
+ def metadata_to_proto(metadata: Metadata) -> ProtoMetadata:
147
+ """Serialize `Metadata` to ProtoBuf."""
148
+ proto = ProtoMetadata( # pylint: disable=E1101
149
+ run_id=metadata.run_id,
150
+ message_id=metadata.message_id,
151
+ src_node_id=metadata.src_node_id,
152
+ dst_node_id=metadata.dst_node_id,
153
+ reply_to_message_id=metadata.reply_to_message_id,
154
+ group_id=metadata.group_id,
155
+ ttl=metadata.ttl,
156
+ message_type=metadata.message_type,
157
+ created_at=metadata.created_at,
158
+ )
159
+ return proto
160
+
161
+
162
+ def metadata_from_proto(metadata_proto: ProtoMetadata) -> Metadata:
163
+ """Deserialize `Metadata` from ProtoBuf."""
164
+ metadata = Metadata(
165
+ run_id=metadata_proto.run_id,
166
+ message_id=metadata_proto.message_id,
167
+ src_node_id=metadata_proto.src_node_id,
168
+ dst_node_id=metadata_proto.dst_node_id,
169
+ reply_to_message_id=metadata_proto.reply_to_message_id,
170
+ group_id=metadata_proto.group_id,
171
+ created_at=metadata_proto.created_at,
172
+ ttl=metadata_proto.ttl,
173
+ message_type=metadata_proto.message_type,
174
+ )
175
+ return metadata
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.
@@ -230,6 +230,7 @@ class Run: # pylint: disable=too-many-instance-attributes
230
230
  running_at: str
231
231
  finished_at: str
232
232
  status: RunStatus
233
+ flwr_aid: str
233
234
 
234
235
  @classmethod
235
236
  def create_empty(cls, run_id: int) -> "Run":
@@ -245,6 +246,7 @@ class Run: # pylint: disable=too-many-instance-attributes
245
246
  running_at="",
246
247
  finished_at="",
247
248
  status=RunStatus(status="", sub_status="", details=""),
249
+ flwr_aid="",
248
250
  )
249
251
 
250
252
 
@@ -289,11 +291,11 @@ class UserAuthCredentials:
289
291
 
290
292
 
291
293
  @dataclass
292
- class UserInfo:
294
+ class AccountInfo:
293
295
  """User information for event log."""
294
296
 
295
- user_id: Optional[str]
296
- user_name: Optional[str]
297
+ flwr_aid: Optional[str]
298
+ account_name: Optional[str]
297
299
 
298
300
 
299
301
  @dataclass
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.
@@ -0,0 +1,15 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Compatibility package containing deprecated legacy components."""
@@ -0,0 +1,15 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Legacy components previously located in ``flwr.client``."""
@@ -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.
@@ -15,38 +15,30 @@
15
15
  """Flower client app."""
16
16
 
17
17
 
18
- import multiprocessing
19
- import os
20
- import sys
21
- import threading
22
18
  import time
23
19
  from contextlib import AbstractContextManager
24
20
  from logging import ERROR, INFO, WARN
25
- from os import urandom
26
21
  from pathlib import Path
27
- from typing import Callable, Optional, Union, cast
22
+ from typing import Callable, Optional, Union
28
23
 
29
- import grpc
30
24
  from cryptography.hazmat.primitives.asymmetric import ec
31
25
  from grpc import RpcError
32
26
 
27
+ from flwr.app.error import Error
33
28
  from flwr.cli.config_utils import get_fab_metadata
34
29
  from flwr.cli.install import install_from_fab
35
30
  from flwr.client.client import Client
36
31
  from flwr.client.client_app import ClientApp, LoadClientAppError
37
- from flwr.client.clientapp.app import flwr_clientapp
38
- from flwr.client.nodestate.nodestate_factory import NodeStateFactory
32
+ from flwr.client.grpc_adapter_client.connection import grpc_adapter
33
+ from flwr.client.grpc_rere_client.connection import grpc_request_response
34
+ from flwr.client.message_handler.message_handler import handle_control_message
35
+ from flwr.client.numpy_client import NumPyClient
36
+ from flwr.client.run_info_store import DeprecatedRunInfoStore
39
37
  from flwr.client.typing import ClientFnExt
40
38
  from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Context, EventType, Message, event
41
39
  from flwr.common.address import parse_address
42
40
  from flwr.common.constant import (
43
- CLIENT_OCTET,
44
- CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
45
- ISOLATION_MODE_PROCESS,
46
- ISOLATION_MODE_SUBPROCESS,
47
41
  MAX_RETRY_DELAY,
48
- RUN_ID_NUM_BYTES,
49
- SERVER_OCTET,
50
42
  TRANSPORT_TYPE_GRPC_ADAPTER,
51
43
  TRANSPORT_TYPE_GRPC_BIDI,
52
44
  TRANSPORT_TYPE_GRPC_RERE,
@@ -55,20 +47,11 @@ from flwr.common.constant import (
55
47
  ErrorCode,
56
48
  )
57
49
  from flwr.common.exit import ExitCode, flwr_exit
58
- from flwr.common.grpc import generic_create_grpc_server
59
50
  from flwr.common.logger import log, warn_deprecated_feature
60
- from flwr.common.message import Error
61
51
  from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
62
52
  from flwr.common.typing import Fab, Run, RunNotRunningException, UserConfig
63
- from flwr.proto.clientappio_pb2_grpc import add_ClientAppIoServicer_to_server
64
-
65
- from .clientapp.clientappio_servicer import ClientAppInputs, ClientAppIoServicer
66
- from .grpc_adapter_client.connection import grpc_adapter
67
- from .grpc_client.connection import grpc_connection
68
- from .grpc_rere_client.connection import grpc_request_response
69
- from .message_handler.message_handler import handle_control_message
70
- from .numpy_client import NumPyClient
71
- from .run_info_store import DeprecatedRunInfoStore
53
+ from flwr.compat.client.grpc_client.connection import grpc_connection
54
+ from flwr.supernode.nodestate import NodeStateFactory
72
55
 
73
56
 
74
57
  def _check_actionable_client(
@@ -158,33 +141,33 @@ def start_client(
158
141
 
159
142
  Examples
160
143
  --------
161
- Starting a gRPC client with an insecure server connection:
162
-
163
- >>> start_client(
164
- >>> server_address=localhost:8080,
165
- >>> client_fn=client_fn,
166
- >>> )
167
-
168
- Starting an SSL-enabled gRPC client using system certificates:
169
-
170
- >>> def client_fn(context: Context):
171
- >>> return FlowerClient().to_client()
172
- >>>
173
- >>> start_client(
174
- >>> server_address=localhost:8080,
175
- >>> client_fn=client_fn,
176
- >>> insecure=False,
177
- >>> )
178
-
179
- Starting an SSL-enabled gRPC client using provided certificates:
180
-
181
- >>> from pathlib import Path
182
- >>>
183
- >>> start_client(
184
- >>> server_address=localhost:8080,
185
- >>> client_fn=client_fn,
186
- >>> root_certificates=Path("/crts/root.pem").read_bytes(),
187
- >>> )
144
+ Starting a gRPC client with an insecure server connection::
145
+
146
+ start_client(
147
+ server_address=localhost:8080,
148
+ client_fn=client_fn,
149
+ )
150
+
151
+ Starting a TLS-enabled gRPC client using system certificates::
152
+
153
+ def client_fn(context: Context):
154
+ return FlowerClient().to_client()
155
+
156
+ start_client(
157
+ server_address=localhost:8080,
158
+ client_fn=client_fn,
159
+ insecure=False,
160
+ )
161
+
162
+ Starting a TLS-enabled gRPC client using provided certificates::
163
+
164
+ from pathlib import Path
165
+
166
+ start_client(
167
+ server_address=localhost:8080,
168
+ client_fn=client_fn,
169
+ root_certificates=Path("/crts/root.pem").read_bytes(),
170
+ )
188
171
  """
189
172
  msg = (
190
173
  "flwr.client.start_client() is deprecated."
@@ -236,8 +219,6 @@ def start_client_internal(
236
219
  max_retries: Optional[int] = None,
237
220
  max_wait_time: Optional[float] = None,
238
221
  flwr_path: Optional[Path] = None,
239
- isolation: Optional[str] = None,
240
- clientappio_api_address: Optional[str] = CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
241
222
  ) -> None:
242
223
  """Start a Flower client node which connects to a Flower server.
243
224
 
@@ -290,17 +271,6 @@ def start_client_internal(
290
271
  If set to None, there is no limit to the total time.
291
272
  flwr_path: Optional[Path] (default: None)
292
273
  The fully resolved path containing installed Flower Apps.
293
- isolation : Optional[str] (default: None)
294
- Isolation mode for `ClientApp`. Possible values are `subprocess` and
295
- `process`. Defaults to `None`, which runs the `ClientApp` in the same process
296
- as the SuperNode. If `subprocess`, the `ClientApp` runs in a subprocess started
297
- by the SueprNode and communicates using gRPC at the address
298
- `clientappio_api_address`. If `process`, the `ClientApp` runs in a separate
299
- isolated process and communicates using gRPC at the address
300
- `clientappio_api_address`.
301
- clientappio_api_address : Optional[str]
302
- (default: `CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS`)
303
- The SuperNode gRPC server address.
304
274
  """
305
275
  if insecure is None:
306
276
  insecure = root_certificates is None
@@ -326,18 +296,6 @@ def start_client_internal(
326
296
 
327
297
  load_client_app_fn = _load_client_app
328
298
 
329
- if isolation:
330
- if clientappio_api_address is None:
331
- raise ValueError(
332
- f"`clientappio_api_address` required when `isolation` is "
333
- f"{ISOLATION_MODE_SUBPROCESS} or {ISOLATION_MODE_PROCESS}",
334
- )
335
- _clientappio_grpc_server, clientappio_servicer = run_clientappio_api_grpc(
336
- address=clientappio_api_address,
337
- certificates=None,
338
- )
339
- clientappio_api_address = cast(str, clientappio_api_address)
340
-
341
299
  # At this point, only `load_client_app_fn` should be used
342
300
  # Both `client` and `client_fn` must not be used directly
343
301
 
@@ -388,7 +346,6 @@ def start_client_internal(
388
346
  run_info_store: Optional[DeprecatedRunInfoStore] = None
389
347
  state_factory = NodeStateFactory()
390
348
  state = state_factory.state()
391
- mp_spawn_context = multiprocessing.get_context("spawn")
392
349
 
393
350
  runs: dict[int, Run] = {}
394
351
 
@@ -473,9 +430,8 @@ def start_client_internal(
473
430
  run: Run = runs[run_id]
474
431
  if get_fab is not None and run.fab_hash:
475
432
  fab = get_fab(run.fab_hash, run_id)
476
- if not isolation:
477
- # If `ClientApp` runs in the same process, install the FAB
478
- install_from_fab(fab.content, flwr_path, True)
433
+ # If `ClientApp` runs in the same process, install the FAB
434
+ install_from_fab(fab.content, flwr_path, True)
479
435
  fab_id, fab_version = get_fab_metadata(fab.content)
480
436
  else:
481
437
  fab = None
@@ -502,73 +458,13 @@ def start_client_internal(
502
458
 
503
459
  # Handle app loading and task message
504
460
  try:
505
- if isolation:
506
- # Two isolation modes:
507
- # 1. `subprocess`: SuperNode is starting the ClientApp
508
- # process as a subprocess.
509
- # 2. `process`: ClientApp process gets started separately
510
- # (via `flwr-clientapp`), for example, in a separate
511
- # Docker container.
512
-
513
- # Generate SuperNode token
514
- token = int.from_bytes(urandom(RUN_ID_NUM_BYTES), "little")
515
-
516
- # Mode 1: SuperNode starts ClientApp as subprocess
517
- start_subprocess = isolation == ISOLATION_MODE_SUBPROCESS
518
-
519
- # Share Message and Context with servicer
520
- clientappio_servicer.set_inputs(
521
- clientapp_input=ClientAppInputs(
522
- message=message,
523
- context=context,
524
- run=run,
525
- fab=fab,
526
- token=token,
527
- ),
528
- token_returned=start_subprocess,
529
- )
530
-
531
- if start_subprocess:
532
- _octet, _colon, _port = (
533
- clientappio_api_address.rpartition(":")
534
- )
535
- io_address = (
536
- f"{CLIENT_OCTET}:{_port}"
537
- if _octet == SERVER_OCTET
538
- else clientappio_api_address
539
- )
540
- # Start ClientApp subprocess
541
- command = [
542
- "flwr-clientapp",
543
- "--clientappio-api-address",
544
- io_address,
545
- "--token",
546
- str(token),
547
- ]
548
- command.append("--insecure")
549
-
550
- proc = mp_spawn_context.Process(
551
- target=_run_flwr_clientapp,
552
- args=(command, os.getpid()),
553
- daemon=True,
554
- )
555
- proc.start()
556
- proc.join()
557
- else:
558
- # Wait for output to become available
559
- while not clientappio_servicer.has_outputs():
560
- time.sleep(0.1)
561
-
562
- outputs = clientappio_servicer.get_outputs()
563
- reply_message, context = outputs.message, outputs.context
564
- else:
565
- # Load ClientApp instance
566
- client_app: ClientApp = load_client_app_fn(
567
- fab_id, fab_version, run.fab_hash
568
- )
461
+ # Load ClientApp instance
462
+ client_app: ClientApp = load_client_app_fn(
463
+ fab_id, fab_version, run.fab_hash
464
+ )
569
465
 
570
- # Execute ClientApp
571
- reply_message = client_app(message=message, context=context)
466
+ # Execute ClientApp
467
+ reply_message = client_app(message=message, context=context)
572
468
  except Exception as ex: # pylint: disable=broad-exception-caught
573
469
 
574
470
  # Legacy grpc-bidi
@@ -684,30 +580,30 @@ def start_numpy_client(
684
580
 
685
581
  Examples
686
582
  --------
687
- Starting a gRPC client with an insecure server connection:
688
-
689
- >>> start_numpy_client(
690
- >>> server_address=localhost:8080,
691
- >>> client=FlowerClient(),
692
- >>> )
693
-
694
- Starting an SSL-enabled gRPC client using system certificates:
695
-
696
- >>> start_numpy_client(
697
- >>> server_address=localhost:8080,
698
- >>> client=FlowerClient(),
699
- >>> insecure=False,
700
- >>> )
701
-
702
- Starting an SSL-enabled gRPC client using provided certificates:
703
-
704
- >>> from pathlib import Path
705
- >>>
706
- >>> start_numpy_client(
707
- >>> server_address=localhost:8080,
708
- >>> client=FlowerClient(),
709
- >>> root_certificates=Path("/crts/root.pem").read_bytes(),
710
- >>> )
583
+ Starting a gRPC client with an insecure server connection::
584
+
585
+ start_numpy_client(
586
+ server_address=localhost:8080,
587
+ client=FlowerClient(),
588
+ )
589
+
590
+ Starting a TLS-enabled gRPC client using system certificates::
591
+
592
+ start_numpy_client(
593
+ server_address=localhost:8080,
594
+ client=FlowerClient(),
595
+ insecure=False,
596
+ )
597
+
598
+ Starting a TLS-enabled gRPC client using provided certificates::
599
+
600
+ from pathlib import Path
601
+
602
+ start_numpy_client(
603
+ server_address=localhost:8080,
604
+ client=FlowerClient(),
605
+ root_certificates=Path("/crts/root.pem").read_bytes(),
606
+ )
711
607
  """
712
608
  mssg = (
713
609
  "flwr.client.start_numpy_client() is deprecated. \n\tInstead, use "
@@ -781,7 +677,7 @@ def _init_connection(transport: Optional[str], server_address: str) -> tuple[
781
677
  try:
782
678
  from requests.exceptions import ConnectionError as RequestsConnectionError
783
679
 
784
- from .rest_client.connection import http_request_response
680
+ from flwr.client.rest_client.connection import http_request_response
785
681
  except ModuleNotFoundError:
786
682
  flwr_exit(ExitCode.COMMON_MISSING_EXTRA_REST)
787
683
  if server_address[:4] != "http":
@@ -792,46 +688,10 @@ def _init_connection(transport: Optional[str], server_address: str) -> tuple[
792
688
  elif transport == TRANSPORT_TYPE_GRPC_ADAPTER:
793
689
  connection, error_type = grpc_adapter, RpcError
794
690
  elif transport == TRANSPORT_TYPE_GRPC_BIDI:
795
- connection, error_type = grpc_connection, RpcError
691
+ connection, error_type = grpc_connection, RpcError # type: ignore[assignment]
796
692
  else:
797
693
  raise ValueError(
798
694
  f"Unknown transport type: {transport} (possible: {TRANSPORT_TYPES})"
799
695
  )
800
696
 
801
697
  return connection, address, error_type
802
-
803
-
804
- def _run_flwr_clientapp(args: list[str], main_pid: int) -> None:
805
- # Monitor the main process in case of SIGKILL
806
- def main_process_monitor() -> None:
807
- while True:
808
- time.sleep(1)
809
- if os.getppid() != main_pid:
810
- os.kill(os.getpid(), 9)
811
-
812
- threading.Thread(target=main_process_monitor, daemon=True).start()
813
-
814
- # Run the command
815
- sys.argv = args
816
- flwr_clientapp()
817
-
818
-
819
- def run_clientappio_api_grpc(
820
- address: str,
821
- certificates: Optional[tuple[bytes, bytes, bytes]],
822
- ) -> tuple[grpc.Server, ClientAppIoServicer]:
823
- """Run ClientAppIo API gRPC server."""
824
- clientappio_servicer: grpc.Server = ClientAppIoServicer()
825
- clientappio_add_servicer_to_server_fn = add_ClientAppIoServicer_to_server
826
- clientappio_grpc_server = generic_create_grpc_server(
827
- servicer_and_add_fn=(
828
- clientappio_servicer,
829
- clientappio_add_servicer_to_server_fn,
830
- ),
831
- server_address=address,
832
- max_message_length=GRPC_MAX_MESSAGE_LENGTH,
833
- certificates=certificates,
834
- )
835
- log(INFO, "Starting Flower ClientAppIo gRPC server on %s", address)
836
- clientappio_grpc_server.start()
837
- return clientappio_grpc_server, clientappio_servicer
@@ -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.