flwr 1.25.0__py3-none-any.whl → 1.26.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 (140) hide show
  1. flwr/__init__.py +1 -1
  2. flwr/app/__init__.py +4 -1
  3. flwr/app/message_type.py +29 -0
  4. flwr/app/metadata.py +5 -2
  5. flwr/app/user_config.py +19 -0
  6. flwr/cli/app.py +37 -19
  7. flwr/cli/app_cmd/publish.py +25 -75
  8. flwr/cli/app_cmd/review.py +18 -69
  9. flwr/cli/auth_plugin/auth_plugin.py +5 -10
  10. flwr/cli/auth_plugin/noop_auth_plugin.py +1 -2
  11. flwr/cli/auth_plugin/oidc_cli_plugin.py +38 -38
  12. flwr/cli/build.py +15 -28
  13. flwr/cli/config/__init__.py +21 -0
  14. flwr/cli/config/ls.py +71 -0
  15. flwr/cli/config_migration.py +297 -0
  16. flwr/cli/config_utils.py +63 -156
  17. flwr/cli/constant.py +71 -0
  18. flwr/cli/federation/__init__.py +0 -2
  19. flwr/cli/federation/ls.py +256 -64
  20. flwr/cli/flower_config.py +429 -0
  21. flwr/cli/install.py +23 -62
  22. flwr/cli/log.py +23 -37
  23. flwr/cli/login/login.py +29 -63
  24. flwr/cli/ls.py +28 -58
  25. flwr/cli/new/new.py +9 -29
  26. flwr/cli/pull.py +19 -37
  27. flwr/cli/run/run.py +85 -93
  28. flwr/cli/run_utils.py +1 -1
  29. flwr/cli/stop.py +32 -73
  30. flwr/cli/supernode/ls.py +25 -57
  31. flwr/cli/supernode/register.py +31 -80
  32. flwr/cli/supernode/unregister.py +24 -70
  33. flwr/cli/typing.py +200 -0
  34. flwr/cli/utils.py +160 -275
  35. flwr/client/grpc_rere_client/connection.py +3 -3
  36. flwr/client/grpc_rere_client/grpc_adapter.py +1 -1
  37. flwr/client/message_handler/message_handler.py +2 -1
  38. flwr/client/mod/centraldp_mods.py +1 -1
  39. flwr/client/mod/localdp_mod.py +1 -1
  40. flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
  41. flwr/client/run_info_store.py +2 -1
  42. flwr/clientapp/client_app.py +2 -1
  43. flwr/common/__init__.py +3 -2
  44. flwr/common/args.py +5 -5
  45. flwr/common/config.py +12 -17
  46. flwr/common/constant.py +3 -16
  47. flwr/common/context.py +2 -1
  48. flwr/common/exit/exit.py +4 -4
  49. flwr/common/exit/exit_code.py +6 -0
  50. flwr/common/grpc.py +2 -1
  51. flwr/common/logger.py +1 -1
  52. flwr/common/message.py +1 -1
  53. flwr/common/retry_invoker.py +13 -5
  54. flwr/common/secure_aggregation/ndarrays_arithmetic.py +5 -2
  55. flwr/common/serde.py +7 -5
  56. flwr/common/telemetry.py +1 -1
  57. flwr/common/typing.py +4 -3
  58. flwr/compat/client/app.py +6 -9
  59. flwr/compat/client/grpc_client/connection.py +2 -1
  60. flwr/compat/common/constant.py +29 -0
  61. flwr/compat/server/app.py +1 -1
  62. flwr/proto/clientappio_pb2.py +2 -2
  63. flwr/proto/clientappio_pb2_grpc.py +104 -88
  64. flwr/proto/clientappio_pb2_grpc.pyi +140 -80
  65. flwr/proto/federation_pb2.py +5 -3
  66. flwr/proto/federation_pb2.pyi +32 -2
  67. flwr/proto/run_pb2.py +5 -13
  68. flwr/proto/run_pb2.pyi +0 -57
  69. flwr/proto/serverappio_pb2.py +2 -2
  70. flwr/proto/serverappio_pb2_grpc.py +138 -207
  71. flwr/proto/serverappio_pb2_grpc.pyi +189 -155
  72. flwr/proto/simulationio_pb2.py +2 -2
  73. flwr/proto/simulationio_pb2_grpc.py +62 -90
  74. flwr/proto/simulationio_pb2_grpc.pyi +95 -55
  75. flwr/server/app.py +6 -13
  76. flwr/server/compat/grid_client_proxy.py +2 -1
  77. flwr/server/grid/grpc_grid.py +5 -5
  78. flwr/server/serverapp/app.py +11 -4
  79. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +1 -1
  80. flwr/server/superlink/fleet/grpc_rere/node_auth_server_interceptor.py +13 -12
  81. flwr/server/superlink/fleet/message_handler/message_handler.py +6 -5
  82. flwr/server/superlink/linkstate/__init__.py +2 -2
  83. flwr/server/superlink/linkstate/in_memory_linkstate.py +2 -10
  84. flwr/server/superlink/linkstate/linkstate.py +2 -21
  85. flwr/server/superlink/linkstate/linkstate_factory.py +16 -8
  86. flwr/server/superlink/linkstate/{sqlite_linkstate.py → sql_linkstate.py} +432 -534
  87. flwr/server/superlink/linkstate/utils.py +49 -2
  88. flwr/server/superlink/serverappio/serverappio_servicer.py +1 -33
  89. flwr/server/superlink/simulation/simulationio_servicer.py +0 -19
  90. flwr/server/utils/validator.py +1 -1
  91. flwr/server/workflow/default_workflows.py +2 -1
  92. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
  93. flwr/serverapp/strategy/bulyan.py +7 -1
  94. flwr/serverapp/strategy/dp_fixed_clipping.py +9 -1
  95. flwr/serverapp/strategy/fedavg.py +1 -1
  96. flwr/serverapp/strategy/fedxgb_cyclic.py +1 -1
  97. flwr/simulation/ray_transport/ray_client_proxy.py +2 -6
  98. flwr/simulation/run_simulation.py +3 -12
  99. flwr/simulation/simulationio_connection.py +3 -3
  100. flwr/{common → supercore}/address.py +7 -33
  101. flwr/supercore/app_utils.py +2 -1
  102. flwr/supercore/constant.py +24 -2
  103. flwr/supercore/corestate/{sqlite_corestate.py → sql_corestate.py} +19 -23
  104. flwr/supercore/credential_store/__init__.py +33 -0
  105. flwr/supercore/credential_store/credential_store.py +34 -0
  106. flwr/supercore/credential_store/file_credential_store.py +76 -0
  107. flwr/{common → supercore}/date.py +0 -11
  108. flwr/supercore/ffs/disk_ffs.py +1 -1
  109. flwr/supercore/object_store/object_store_factory.py +14 -6
  110. flwr/supercore/object_store/{sqlite_object_store.py → sql_object_store.py} +115 -117
  111. flwr/supercore/sql_mixin.py +315 -0
  112. flwr/supercore/state/__init__.py +15 -0
  113. flwr/supercore/state/alembic/__init__.py +15 -0
  114. flwr/supercore/state/alembic/env.py +103 -0
  115. flwr/supercore/state/alembic/script.py.mako +43 -0
  116. flwr/supercore/state/alembic/utils.py +239 -0
  117. flwr/supercore/state/alembic/versions/__init__.py +15 -0
  118. flwr/supercore/state/alembic/versions/rev_2026_01_28_initialize_migration_of_state_tables.py +200 -0
  119. flwr/supercore/state/schema/README.md +121 -0
  120. flwr/supercore/state/schema/__init__.py +15 -0
  121. flwr/supercore/state/schema/corestate_tables.py +36 -0
  122. flwr/supercore/state/schema/linkstate_tables.py +152 -0
  123. flwr/supercore/state/schema/objectstore_tables.py +90 -0
  124. flwr/supercore/superexec/run_superexec.py +2 -2
  125. flwr/supercore/utils.py +36 -1
  126. flwr/superlink/federation/federation_manager.py +2 -2
  127. flwr/superlink/federation/noop_federation_manager.py +8 -6
  128. flwr/superlink/servicer/control/control_servicer.py +19 -17
  129. flwr/supernode/cli/flower_supernode.py +2 -1
  130. flwr/supernode/runtime/run_clientapp.py +14 -14
  131. flwr/supernode/servicer/clientappio/clientappio_servicer.py +10 -8
  132. flwr/supernode/start_client_internal.py +10 -6
  133. {flwr-1.25.0.dist-info → flwr-1.26.0.dist-info}/METADATA +7 -5
  134. {flwr-1.25.0.dist-info → flwr-1.26.0.dist-info}/RECORD +137 -116
  135. flwr/cli/federation/show.py +0 -318
  136. flwr/common/pyproject.py +0 -42
  137. flwr/supercore/sqlite_mixin.py +0 -159
  138. /flwr/{common → supercore}/version.py +0 -0
  139. {flwr-1.25.0.dist-info → flwr-1.26.0.dist-info}/WHEEL +0 -0
  140. {flwr-1.25.0.dist-info → flwr-1.26.0.dist-info}/entry_points.txt +0 -0
@@ -28,7 +28,6 @@ from flwr.common.constant import (
28
28
  SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS,
29
29
  SUPERLINK_NODE_ID,
30
30
  ErrorCode,
31
- MessageType,
32
31
  )
33
32
  from flwr.common.grpc import create_channel, on_channel_state_change
34
33
  from flwr.common.inflatable import (
@@ -50,7 +49,7 @@ from flwr.common.inflatable_utils import (
50
49
  )
51
50
  from flwr.common.logger import log, warn_deprecated_feature
52
51
  from flwr.common.message import make_message, remove_content_from_message
53
- from flwr.common.retry_invoker import _make_simple_grpc_retry_invoker, _wrap_stub
52
+ from flwr.common.retry_invoker import make_simple_grpc_retry_invoker, wrap_stub
54
53
  from flwr.common.serde import message_to_proto, run_from_proto
55
54
  from flwr.common.typing import Run
56
55
  from flwr.proto.appio_pb2 import ( # pylint: disable=E0611
@@ -69,6 +68,7 @@ from flwr.proto.serverappio_pb2 import ( # pylint: disable=E0611
69
68
  GetNodesResponse,
70
69
  )
71
70
  from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub # pylint: disable=E0611
71
+ from flwr.supercore.constant import SYSTEM_MESSAGE_TYPE
72
72
 
73
73
  from .grid import Grid
74
74
 
@@ -127,7 +127,7 @@ class GrpcGrid(Grid):
127
127
  self._grpc_stub: ServerAppIoStub | None = None
128
128
  self._channel: grpc.Channel | None = None
129
129
  self.node = Node(node_id=SUPERLINK_NODE_ID)
130
- self._retry_invoker = _make_simple_grpc_retry_invoker()
130
+ self._retry_invoker = make_simple_grpc_retry_invoker()
131
131
  super().__init__()
132
132
 
133
133
  @property
@@ -150,7 +150,7 @@ class GrpcGrid(Grid):
150
150
  )
151
151
  self._channel.subscribe(on_channel_state_change)
152
152
  self._grpc_stub = ServerAppIoStub(self._channel)
153
- _wrap_stub(self._grpc_stub, self._retry_invoker)
153
+ wrap_stub(self._grpc_stub, self._retry_invoker)
154
154
  log(DEBUG, "[flwr-serverapp] Connected to %s", self._addr)
155
155
 
156
156
  def _disconnect(self) -> None:
@@ -341,7 +341,7 @@ class GrpcGrid(Grid):
341
341
  message_id="",
342
342
  src_node_id=self.node.node_id,
343
343
  dst_node_id=self.node.node_id,
344
- message_type=MessageType.SYSTEM,
344
+ message_type=SYSTEM_MESSAGE_TYPE,
345
345
  group_id="",
346
346
  ttl=0,
347
347
  reply_to_message_id=msg_proto.metadata.reply_to_message_id,
@@ -144,6 +144,10 @@ def run_serverapp( # pylint: disable=R0913, R0914, R0915, R0917, W0212
144
144
  exit_code = ExitCode.SUCCESS
145
145
 
146
146
  def on_exit() -> None:
147
+ # Set Grpc max retries to 1 to avoid blocking on exit
148
+ if grid:
149
+ grid._retry_invoker.max_tries = 1
150
+
147
151
  # Stop heartbeat sender
148
152
  if heartbeat_sender and heartbeat_sender.is_running:
149
153
  heartbeat_sender.stop()
@@ -154,10 +158,13 @@ def run_serverapp( # pylint: disable=R0913, R0914, R0915, R0917, W0212
154
158
 
155
159
  # Update run status
156
160
  if run and run_status and grid:
157
- run_status_proto = run_status_to_proto(run_status)
158
- grid._stub.UpdateRunStatus(
159
- UpdateRunStatusRequest(run_id=run.run_id, run_status=run_status_proto)
160
- )
161
+ try:
162
+ req = UpdateRunStatusRequest(
163
+ run_id=run.run_id, run_status=run_status_to_proto(run_status)
164
+ )
165
+ grid._stub.UpdateRunStatus(req)
166
+ except grpc.RpcError:
167
+ pass
161
168
 
162
169
  # Close the Grpc connection
163
170
  if grid:
@@ -30,7 +30,6 @@ from flwr.common.constant import (
30
30
  GRPC_ADAPTER_METADATA_MESSAGE_QUALNAME_KEY,
31
31
  )
32
32
  from flwr.common.logger import log
33
- from flwr.common.version import package_name, package_version
34
33
  from flwr.proto import grpcadapter_pb2_grpc # pylint: disable=E0611
35
34
  from flwr.proto.fab_pb2 import GetFabRequest # pylint: disable=E0611
36
35
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
@@ -49,6 +48,7 @@ from flwr.proto.message_pb2 import ( # pylint: disable=E0611
49
48
  PushObjectRequest,
50
49
  )
51
50
  from flwr.proto.run_pb2 import GetRunRequest # pylint: disable=E0611
51
+ from flwr.supercore.version import package_name, package_version
52
52
 
53
53
  from ..grpc_rere.fleet_servicer import FleetServicer
54
54
 
@@ -108,7 +108,7 @@ class NodeAuthServerInterceptor(grpc.ServerInterceptor): # type: ignore
108
108
  def _wrap_method_handler(
109
109
  self,
110
110
  method_handler: grpc.RpcMethodHandler,
111
- expected_public_key: bytes,
111
+ received_public_key: bytes,
112
112
  ) -> grpc.RpcMethodHandler:
113
113
  def _generic_method_handler(
114
114
  request: GrpcMessage,
@@ -117,21 +117,22 @@ class NodeAuthServerInterceptor(grpc.ServerInterceptor): # type: ignore
117
117
  # Note: This function runs in a different thread
118
118
  # than the `intercept_service` function.
119
119
 
120
- # Retrieve the public key
121
- if isinstance(request, (RegisterNodeFleetRequest | ActivateNodeRequest)):
122
- actual_public_key = request.public_key
123
- else:
120
+ # Skip registration and activation requests
121
+ if not isinstance(request, (RegisterNodeFleetRequest, ActivateNodeRequest)):
122
+ # Retrieve the node ID from the request
124
123
  if hasattr(request, "node"):
125
- node_id = request.node.node_id
124
+ received_node_id = request.node.node_id
126
125
  else:
127
- node_id = request.node_id # type: ignore[attr-defined]
128
- actual_public_key = self.state_factory.state().get_node_public_key(
129
- node_id
126
+ received_node_id = request.node_id # type: ignore[attr-defined]
127
+
128
+ # Get the actual node ID based on the received public key
129
+ node_id = self.state_factory.state().get_node_id_by_public_key(
130
+ received_public_key
130
131
  )
131
132
 
132
- # Verify the public key
133
- if actual_public_key != expected_public_key:
134
- context.abort(grpc.StatusCode.UNAUTHENTICATED, "Invalid node ID")
133
+ # Verify that the received node ID matches the actual node ID
134
+ if received_node_id != node_id:
135
+ context.abort(grpc.StatusCode.UNAUTHENTICATED, "Invalid node ID")
135
136
 
136
137
  response: GrpcMessage = method_handler.unary_unary(request, context)
137
138
  return response
@@ -166,13 +166,13 @@ def pull_messages( # pylint: disable=too-many-locals
166
166
  response = PullMessagesResponse(messages_list=msg_proto, message_object_trees=trees)
167
167
 
168
168
  # Record incoming traffic size
169
- bytes_recv = len(request.SerializeToString())
169
+ bytes_recv = request.ByteSize()
170
170
 
171
171
  # Record traffic only if message was successfully processed
172
172
  # All messages in this request are assumed to belong to the same run
173
173
  if run_id_to_record is not None:
174
174
  # Record outgoing traffic size
175
- bytes_sent = len(response.SerializeToString())
175
+ bytes_sent = response.ByteSize()
176
176
  state.store_traffic(
177
177
  run_id_to_record, bytes_sent=bytes_sent, bytes_recv=bytes_recv
178
178
  )
@@ -191,7 +191,7 @@ def push_messages(
191
191
  run_id = msg.metadata.run_id
192
192
 
193
193
  # Record incoming traffic size
194
- bytes_recv = len(request.SerializeToString())
194
+ bytes_recv = request.ByteSize()
195
195
 
196
196
  # Abort if the run is not running
197
197
  abort_msg = check_abort(
@@ -218,7 +218,7 @@ def push_messages(
218
218
  )
219
219
 
220
220
  # Record outgoing traffic size
221
- bytes_sent = len(response.SerializeToString())
221
+ bytes_sent = response.ByteSize()
222
222
 
223
223
  # Record traffic only if message was successfully processed
224
224
  # Only one message is processed per request
@@ -321,7 +321,8 @@ def pull_object(
321
321
  if content is not None:
322
322
  object_available = content != b""
323
323
  # Record bytes traffic pulled by SuperNode
324
- state.store_traffic(request.run_id, bytes_sent=len(content), bytes_recv=0)
324
+ if object_available:
325
+ state.store_traffic(request.run_id, bytes_sent=len(content), bytes_recv=0)
325
326
  return PullObjectResponse(
326
327
  object_found=True,
327
328
  object_available=object_available,
@@ -18,11 +18,11 @@
18
18
  from .in_memory_linkstate import InMemoryLinkState as InMemoryLinkState
19
19
  from .linkstate import LinkState as LinkState
20
20
  from .linkstate_factory import LinkStateFactory as LinkStateFactory
21
- from .sqlite_linkstate import SqliteLinkState as SqliteLinkState
21
+ from .sql_linkstate import SqlLinkState as SqlLinkState
22
22
 
23
23
  __all__ = [
24
24
  "InMemoryLinkState",
25
25
  "LinkState",
26
26
  "LinkStateFactory",
27
- "SqliteLinkState",
27
+ "SqlLinkState",
28
28
  ]
@@ -23,6 +23,7 @@ from dataclasses import dataclass, field
23
23
  from datetime import datetime, timezone
24
24
  from logging import ERROR, WARNING
25
25
 
26
+ from flwr.app.user_config import UserConfig
26
27
  from flwr.common import Context, Message, log, now
27
28
  from flwr.common.constant import (
28
29
  HEARTBEAT_PATIENCE,
@@ -35,7 +36,7 @@ from flwr.common.constant import (
35
36
  SubStatus,
36
37
  )
37
38
  from flwr.common.record import ConfigRecord
38
- from flwr.common.typing import Run, RunStatus, UserConfig
39
+ from flwr.common.typing import Run, RunStatus
39
40
  from flwr.proto.node_pb2 import NodeInfo # pylint: disable=E0611
40
41
  from flwr.server.superlink.linkstate.linkstate import LinkState
41
42
  from flwr.server.utils import validate_message
@@ -518,15 +519,6 @@ class InMemoryLinkState(LinkState, InMemoryCoreState): # pylint: disable=R0902,
518
519
  node.online_until, tz=timezone.utc
519
520
  ).isoformat()
520
521
 
521
- def get_node_public_key(self, node_id: int) -> bytes:
522
- """Get `public_key` for the specified `node_id`."""
523
- with self.lock:
524
- if (
525
- node := self.nodes.get(node_id)
526
- ) is None or node.status == NodeStatus.UNREGISTERED:
527
- raise ValueError(f"Node ID {node_id} not found")
528
- return node.public_key
529
-
530
522
  def get_node_id_by_public_key(self, public_key: bytes) -> int | None:
531
523
  """Get `node_id` for the specified `public_key` if it exists and is not
532
524
  deleted."""
@@ -18,9 +18,10 @@
18
18
  import abc
19
19
  from collections.abc import Sequence
20
20
 
21
+ from flwr.app.user_config import UserConfig
21
22
  from flwr.common import Context, Message
22
23
  from flwr.common.record import ConfigRecord
23
- from flwr.common.typing import Run, RunStatus, UserConfig
24
+ from flwr.common.typing import Run, RunStatus
24
25
  from flwr.proto.node_pb2 import NodeInfo # pylint: disable=E0611
25
26
  from flwr.supercore.corestate import CoreState
26
27
  from flwr.superlink.federation import FederationManager
@@ -245,26 +246,6 @@ class LinkState(CoreState): # pylint: disable=R0904
245
246
  the specified filters.
246
247
  """
247
248
 
248
- @abc.abstractmethod
249
- def get_node_public_key(self, node_id: int) -> bytes:
250
- """Get `public_key` for the specified `node_id`.
251
-
252
- Parameters
253
- ----------
254
- node_id : int
255
- The identifier of the node whose public key is to be retrieved.
256
-
257
- Returns
258
- -------
259
- bytes
260
- The public key associated with the specified `node_id`.
261
-
262
- Raises
263
- ------
264
- ValueError
265
- If the specified `node_id` does not exist in the link state.
266
- """
267
-
268
249
  @abc.abstractmethod
269
250
  def create_run( # pylint: disable=too-many-arguments,too-many-positional-arguments
270
251
  self,
@@ -24,7 +24,7 @@ from flwr.superlink.federation import FederationManager
24
24
 
25
25
  from .in_memory_linkstate import InMemoryLinkState
26
26
  from .linkstate import LinkState
27
- from .sqlite_linkstate import SqliteLinkState
27
+ from .sql_linkstate import SqlLinkState
28
28
 
29
29
 
30
30
  class LinkStateFactory:
@@ -56,20 +56,28 @@ class LinkStateFactory:
56
56
 
57
57
  def state(self) -> LinkState:
58
58
  """Return a State instance and create it, if necessary."""
59
+ # Return cached state if it exists
60
+ if self.state_instance is not None:
61
+ if self.database == FLWR_IN_MEMORY_DB_NAME:
62
+ log(DEBUG, "Using InMemoryState")
63
+ else:
64
+ log(DEBUG, "Using SqlLinkState")
65
+ return self.state_instance
66
+
59
67
  # Get the ObjectStore instance
60
68
  object_store = self.objectstore_factory.store()
61
69
 
62
70
  # InMemoryState
63
71
  if self.database == FLWR_IN_MEMORY_DB_NAME:
64
- if self.state_instance is None:
65
- self.state_instance = InMemoryLinkState(
66
- self.federation_manager, object_store
67
- )
72
+ self.state_instance = InMemoryLinkState(
73
+ self.federation_manager, object_store
74
+ )
68
75
  log(DEBUG, "Using InMemoryState")
69
76
  return self.state_instance
70
77
 
71
- # SqliteState
72
- state = SqliteLinkState(self.database, self.federation_manager, object_store)
78
+ # SqlLinkState
79
+ state = SqlLinkState(self.database, self.federation_manager, object_store)
73
80
  state.initialize()
74
- log(DEBUG, "Using SqliteState")
81
+ self.state_instance = state
82
+ log(DEBUG, "Using SqlLinkState")
75
83
  return state