flwr-nightly 1.17.0.dev20250318__py3-none-any.whl → 1.17.0.dev20250319__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.
- flwr/common/constant.py +2 -0
- flwr/common/logger.py +2 -2
- flwr/server/__init__.py +2 -0
- flwr/server/compat/__init__.py +2 -2
- flwr/server/compat/app.py +11 -11
- flwr/server/compat/app_utils.py +16 -16
- flwr/server/compat/grid_client_proxy.py +8 -8
- flwr/server/grid/__init__.py +7 -6
- flwr/server/grid/grid.py +43 -14
- flwr/server/grid/grpc_grid.py +11 -10
- flwr/server/grid/inmemory_grid.py +5 -5
- flwr/server/run_serverapp.py +4 -4
- flwr/server/server_app.py +37 -11
- flwr/server/serverapp/app.py +10 -10
- flwr/server/superlink/linkstate/in_memory_linkstate.py +28 -3
- flwr/server/superlink/linkstate/sqlite_linkstate.py +40 -2
- flwr/server/superlink/linkstate/utils.py +67 -10
- flwr/server/typing.py +3 -3
- flwr/server/workflow/default_workflows.py +17 -19
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +15 -15
- flwr/simulation/run_simulation.py +10 -10
- {flwr_nightly-1.17.0.dev20250318.dist-info → flwr_nightly-1.17.0.dev20250319.dist-info}/METADATA +1 -1
- {flwr_nightly-1.17.0.dev20250318.dist-info → flwr_nightly-1.17.0.dev20250319.dist-info}/RECORD +26 -26
- {flwr_nightly-1.17.0.dev20250318.dist-info → flwr_nightly-1.17.0.dev20250319.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.17.0.dev20250318.dist-info → flwr_nightly-1.17.0.dev20250319.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.17.0.dev20250318.dist-info → flwr_nightly-1.17.0.dev20250319.dist-info}/entry_points.txt +0 -0
flwr/common/constant.py
CHANGED
@@ -61,6 +61,7 @@ PING_CALL_TIMEOUT = 5
|
|
61
61
|
PING_BASE_MULTIPLIER = 0.8
|
62
62
|
PING_RANDOM_RANGE = (-0.1, 0.1)
|
63
63
|
PING_MAX_INTERVAL = 1e300
|
64
|
+
PING_PATIENCE = 2
|
64
65
|
|
65
66
|
# IDs
|
66
67
|
RUN_ID_NUM_BYTES = 8
|
@@ -166,6 +167,7 @@ class ErrorCode:
|
|
166
167
|
CLIENT_APP_RAISED_EXCEPTION = 2
|
167
168
|
MESSAGE_UNAVAILABLE = 3
|
168
169
|
REPLY_MESSAGE_UNAVAILABLE = 4
|
170
|
+
NODE_UNAVAILABLE = 5
|
169
171
|
|
170
172
|
def __new__(cls) -> ErrorCode:
|
171
173
|
"""Prevent instantiation."""
|
flwr/common/logger.py
CHANGED
@@ -250,9 +250,9 @@ def warn_deprecated_feature_with_example(
|
|
250
250
|
log(
|
251
251
|
WARN,
|
252
252
|
"""FEATURE UPDATE: %s
|
253
|
-
|
253
|
+
------------------------------------------------------------
|
254
254
|
%s
|
255
|
-
|
255
|
+
------------------------------------------------------------
|
256
256
|
""",
|
257
257
|
example_message,
|
258
258
|
code_example,
|
flwr/server/__init__.py
CHANGED
@@ -22,6 +22,7 @@ from .client_manager import ClientManager as ClientManager
|
|
22
22
|
from .client_manager import SimpleClientManager as SimpleClientManager
|
23
23
|
from .compat import LegacyContext as LegacyContext
|
24
24
|
from .grid import Driver as Driver
|
25
|
+
from .grid import Grid as Grid
|
25
26
|
from .history import History as History
|
26
27
|
from .server import Server as Server
|
27
28
|
from .server_app import ServerApp as ServerApp
|
@@ -31,6 +32,7 @@ from .serverapp_components import ServerAppComponents as ServerAppComponents
|
|
31
32
|
__all__ = [
|
32
33
|
"ClientManager",
|
33
34
|
"Driver",
|
35
|
+
"Grid",
|
34
36
|
"History",
|
35
37
|
"LegacyContext",
|
36
38
|
"Server",
|
flwr/server/compat/__init__.py
CHANGED
@@ -15,10 +15,10 @@
|
|
15
15
|
"""Flower ServerApp compatibility package."""
|
16
16
|
|
17
17
|
|
18
|
-
from .app import
|
18
|
+
from .app import start_grid as start_grid
|
19
19
|
from .legacy_context import LegacyContext as LegacyContext
|
20
20
|
|
21
21
|
__all__ = [
|
22
22
|
"LegacyContext",
|
23
|
-
"
|
23
|
+
"start_grid",
|
24
24
|
]
|
flwr/server/compat/app.py
CHANGED
@@ -12,7 +12,7 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
# ==============================================================================
|
15
|
-
"""Flower
|
15
|
+
"""Flower grid app."""
|
16
16
|
|
17
17
|
|
18
18
|
from logging import INFO
|
@@ -25,27 +25,27 @@ from flwr.server.server import Server, init_defaults, run_fl
|
|
25
25
|
from flwr.server.server_config import ServerConfig
|
26
26
|
from flwr.server.strategy import Strategy
|
27
27
|
|
28
|
-
from ..grid import
|
28
|
+
from ..grid import Grid
|
29
29
|
from .app_utils import start_update_client_manager_thread
|
30
30
|
|
31
31
|
|
32
|
-
def
|
32
|
+
def start_grid( # pylint: disable=too-many-arguments, too-many-locals
|
33
33
|
*,
|
34
|
-
|
34
|
+
grid: Grid,
|
35
35
|
server: Optional[Server] = None,
|
36
36
|
config: Optional[ServerConfig] = None,
|
37
37
|
strategy: Optional[Strategy] = None,
|
38
38
|
client_manager: Optional[ClientManager] = None,
|
39
39
|
) -> History:
|
40
|
-
"""Start a Flower
|
40
|
+
"""Start a Flower server.
|
41
41
|
|
42
42
|
Parameters
|
43
43
|
----------
|
44
|
-
|
45
|
-
The
|
44
|
+
grid : Grid
|
45
|
+
The Grid object to use.
|
46
46
|
server : Optional[flwr.server.Server] (default: None)
|
47
47
|
A server implementation, either `flwr.server.Server` or a subclass
|
48
|
-
thereof. If no instance is provided, then `
|
48
|
+
thereof. If no instance is provided, then `start_grid` will create
|
49
49
|
one.
|
50
50
|
config : Optional[ServerConfig] (default: None)
|
51
51
|
Currently supported values are `num_rounds` (int, default: 1) and
|
@@ -56,7 +56,7 @@ def start_driver( # pylint: disable=too-many-arguments, too-many-locals
|
|
56
56
|
`start_server` will use `flwr.server.strategy.FedAvg`.
|
57
57
|
client_manager : Optional[flwr.server.ClientManager] (default: None)
|
58
58
|
An implementation of the class `flwr.server.ClientManager`. If no
|
59
|
-
implementation is provided, then `
|
59
|
+
implementation is provided, then `start_grid` will use
|
60
60
|
`flwr.server.SimpleClientManager`.
|
61
61
|
|
62
62
|
Returns
|
@@ -64,7 +64,7 @@ def start_driver( # pylint: disable=too-many-arguments, too-many-locals
|
|
64
64
|
hist : flwr.server.history.History
|
65
65
|
Object containing training and evaluation metrics.
|
66
66
|
"""
|
67
|
-
# Initialize the
|
67
|
+
# Initialize the server and config
|
68
68
|
initialized_server, initialized_config = init_defaults(
|
69
69
|
server=server,
|
70
70
|
config=config,
|
@@ -80,7 +80,7 @@ def start_driver( # pylint: disable=too-many-arguments, too-many-locals
|
|
80
80
|
|
81
81
|
# Start the thread updating nodes
|
82
82
|
thread, f_stop, c_done = start_update_client_manager_thread(
|
83
|
-
|
83
|
+
grid, initialized_server.client_manager()
|
84
84
|
)
|
85
85
|
|
86
86
|
# Wait until the node registration done
|
flwr/server/compat/app_utils.py
CHANGED
@@ -12,7 +12,7 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
# ==============================================================================
|
15
|
-
"""Utility functions for the `
|
15
|
+
"""Utility functions for the `start_grid`."""
|
16
16
|
|
17
17
|
|
18
18
|
import threading
|
@@ -20,18 +20,18 @@ import threading
|
|
20
20
|
from flwr.common.typing import RunNotRunningException
|
21
21
|
|
22
22
|
from ..client_manager import ClientManager
|
23
|
-
from ..grid import
|
24
|
-
from .grid_client_proxy import
|
23
|
+
from ..grid import Grid
|
24
|
+
from .grid_client_proxy import GridClientProxy
|
25
25
|
|
26
26
|
|
27
27
|
def start_update_client_manager_thread(
|
28
|
-
|
28
|
+
grid: Grid,
|
29
29
|
client_manager: ClientManager,
|
30
30
|
) -> tuple[threading.Thread, threading.Event, threading.Event]:
|
31
31
|
"""Periodically update the nodes list in the client manager in a thread.
|
32
32
|
|
33
|
-
This function starts a thread that periodically uses the associated
|
34
|
-
get all node_ids. Each node_id is then converted into a `
|
33
|
+
This function starts a thread that periodically uses the associated grid to
|
34
|
+
get all node_ids. Each node_id is then converted into a `GridClientProxy`
|
35
35
|
instance and stored in the `registered_nodes` dictionary with node_id as key.
|
36
36
|
|
37
37
|
New nodes will be added to the ClientManager via `client_manager.register()`,
|
@@ -40,8 +40,8 @@ def start_update_client_manager_thread(
|
|
40
40
|
|
41
41
|
Parameters
|
42
42
|
----------
|
43
|
-
|
44
|
-
The
|
43
|
+
grid : Grid
|
44
|
+
The Grid object to use.
|
45
45
|
client_manager : ClientManager
|
46
46
|
The ClientManager object to be updated.
|
47
47
|
|
@@ -59,7 +59,7 @@ def start_update_client_manager_thread(
|
|
59
59
|
thread = threading.Thread(
|
60
60
|
target=_update_client_manager,
|
61
61
|
args=(
|
62
|
-
|
62
|
+
grid,
|
63
63
|
client_manager,
|
64
64
|
f_stop,
|
65
65
|
c_done,
|
@@ -72,17 +72,17 @@ def start_update_client_manager_thread(
|
|
72
72
|
|
73
73
|
|
74
74
|
def _update_client_manager(
|
75
|
-
|
75
|
+
grid: Grid,
|
76
76
|
client_manager: ClientManager,
|
77
77
|
f_stop: threading.Event,
|
78
78
|
c_done: threading.Event,
|
79
79
|
) -> None:
|
80
80
|
"""Update the nodes list in the client manager."""
|
81
|
-
# Loop until the
|
82
|
-
registered_nodes: dict[int,
|
81
|
+
# Loop until the grid is disconnected
|
82
|
+
registered_nodes: dict[int, GridClientProxy] = {}
|
83
83
|
while not f_stop.is_set():
|
84
84
|
try:
|
85
|
-
all_node_ids = set(
|
85
|
+
all_node_ids = set(grid.get_node_ids())
|
86
86
|
except RunNotRunningException:
|
87
87
|
f_stop.set()
|
88
88
|
break
|
@@ -97,10 +97,10 @@ def _update_client_manager(
|
|
97
97
|
|
98
98
|
# Register new nodes
|
99
99
|
for node_id in new_nodes:
|
100
|
-
client_proxy =
|
100
|
+
client_proxy = GridClientProxy(
|
101
101
|
node_id=node_id,
|
102
|
-
|
103
|
-
run_id=
|
102
|
+
grid=grid,
|
103
|
+
run_id=grid.run.run_id,
|
104
104
|
)
|
105
105
|
if client_manager.register(client_proxy):
|
106
106
|
registered_nodes[node_id] = client_proxy
|
@@ -12,7 +12,7 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
# ==============================================================================
|
15
|
-
"""Flower ClientProxy implementation
|
15
|
+
"""Flower ClientProxy implementation using Grid."""
|
16
16
|
|
17
17
|
|
18
18
|
from typing import Optional
|
@@ -22,16 +22,16 @@ from flwr.common import Message, MessageType, MessageTypeLegacy, RecordSet
|
|
22
22
|
from flwr.common import recordset_compat as compat
|
23
23
|
from flwr.server.client_proxy import ClientProxy
|
24
24
|
|
25
|
-
from ..grid.grid import
|
25
|
+
from ..grid.grid import Grid
|
26
26
|
|
27
27
|
|
28
|
-
class
|
29
|
-
"""Flower client proxy which delegates work using
|
28
|
+
class GridClientProxy(ClientProxy):
|
29
|
+
"""Flower client proxy which delegates work using Grid."""
|
30
30
|
|
31
|
-
def __init__(self, node_id: int,
|
31
|
+
def __init__(self, node_id: int, grid: Grid, run_id: int):
|
32
32
|
super().__init__(str(node_id))
|
33
33
|
self.node_id = node_id
|
34
|
-
self.
|
34
|
+
self.grid = grid
|
35
35
|
self.run_id = run_id
|
36
36
|
|
37
37
|
def get_properties(
|
@@ -110,7 +110,7 @@ class DriverClientProxy(ClientProxy):
|
|
110
110
|
) -> RecordSet:
|
111
111
|
|
112
112
|
# Create message
|
113
|
-
message = self.
|
113
|
+
message = self.grid.create_message(
|
114
114
|
content=recordset,
|
115
115
|
message_type=message_type,
|
116
116
|
dst_node_id=self.node_id,
|
@@ -119,7 +119,7 @@ class DriverClientProxy(ClientProxy):
|
|
119
119
|
)
|
120
120
|
|
121
121
|
# Send message and wait for reply
|
122
|
-
messages = list(self.
|
122
|
+
messages = list(self.grid.send_and_receive(messages=[message]))
|
123
123
|
|
124
124
|
# A single reply is expected
|
125
125
|
if len(messages) != 1:
|
flwr/server/grid/__init__.py
CHANGED
@@ -12,15 +12,16 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
# ==============================================================================
|
15
|
-
"""Flower
|
15
|
+
"""Flower grid SDK."""
|
16
16
|
|
17
17
|
|
18
|
-
from .grid import Driver
|
19
|
-
from .grpc_grid import
|
20
|
-
from .inmemory_grid import
|
18
|
+
from .grid import Driver, Grid
|
19
|
+
from .grpc_grid import GrpcGrid
|
20
|
+
from .inmemory_grid import InMemoryGrid
|
21
21
|
|
22
22
|
__all__ = [
|
23
23
|
"Driver",
|
24
|
-
"
|
25
|
-
"
|
24
|
+
"Grid",
|
25
|
+
"GrpcGrid",
|
26
|
+
"InMemoryGrid",
|
26
27
|
]
|
flwr/server/grid/grid.py
CHANGED
@@ -12,7 +12,7 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
# ==============================================================================
|
15
|
-
"""
|
15
|
+
"""Grid (abstract base class)."""
|
16
16
|
|
17
17
|
|
18
18
|
from abc import ABC, abstractmethod
|
@@ -23,21 +23,21 @@ from flwr.common import Message, RecordSet
|
|
23
23
|
from flwr.common.typing import Run
|
24
24
|
|
25
25
|
|
26
|
-
class
|
27
|
-
"""Abstract base
|
26
|
+
class Grid(ABC):
|
27
|
+
"""Abstract base class Grid to send/receive messages."""
|
28
28
|
|
29
29
|
@abstractmethod
|
30
30
|
def set_run(self, run_id: int) -> None:
|
31
31
|
"""Request a run to the SuperLink with a given `run_id`.
|
32
32
|
|
33
|
-
If a Run with the specified
|
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
|
35
|
+
in the grid, such as sending ``Message``s.
|
36
36
|
|
37
37
|
Parameters
|
38
38
|
----------
|
39
39
|
run_id : int
|
40
|
-
The
|
40
|
+
The ``run_id`` of the ``Run`` this ``Grid`` object operates in.
|
41
41
|
"""
|
42
42
|
|
43
43
|
@property
|
@@ -56,8 +56,8 @@ class Driver(ABC):
|
|
56
56
|
) -> Message:
|
57
57
|
"""Create a new message with specified parameters.
|
58
58
|
|
59
|
-
This method constructs a new
|
60
|
-
The
|
59
|
+
This method constructs a new ``Message`` with given content and metadata.
|
60
|
+
The ``run_id`` and ``src_node_id`` will be set automatically.
|
61
61
|
|
62
62
|
Parameters
|
63
63
|
----------
|
@@ -71,12 +71,12 @@ class Driver(ABC):
|
|
71
71
|
The ID of the destination node to which the message is being sent.
|
72
72
|
group_id : str
|
73
73
|
The ID of the group to which this message is associated. In some settings,
|
74
|
-
this is used as the
|
74
|
+
this is used as the federated learning round.
|
75
75
|
ttl : Optional[float] (default: None)
|
76
76
|
Time-to-live for the round trip of this message, i.e., the time from sending
|
77
77
|
this message to receiving a reply. It specifies in seconds the duration for
|
78
78
|
which the message and its potential reply are considered valid. If unset,
|
79
|
-
the default TTL (i.e.,
|
79
|
+
the default TTL (i.e., ``common.DEFAULT_TTL``) will be used.
|
80
80
|
|
81
81
|
Returns
|
82
82
|
-------
|
@@ -93,7 +93,7 @@ class Driver(ABC):
|
|
93
93
|
"""Push messages to specified node IDs.
|
94
94
|
|
95
95
|
This method takes an iterable of messages and sends each message
|
96
|
-
to the node specified in
|
96
|
+
to the node specified in ``dst_node_id``.
|
97
97
|
|
98
98
|
Parameters
|
99
99
|
----------
|
@@ -154,8 +154,37 @@ class Driver(ABC):
|
|
154
154
|
|
155
155
|
Notes
|
156
156
|
-----
|
157
|
-
This method uses
|
158
|
-
to collect the replies. If
|
157
|
+
This method uses ``push_messages`` to send the messages and ``pull_messages``
|
158
|
+
to collect the replies. If ``timeout`` is set, the method may not return
|
159
159
|
replies for all sent messages. A message remains valid until its TTL,
|
160
|
-
which is not affected by
|
160
|
+
which is not affected by ``timeout``.
|
161
161
|
"""
|
162
|
+
|
163
|
+
|
164
|
+
class Driver(Grid):
|
165
|
+
"""Deprecated abstract base class ``Driver``, use ``Grid`` instead.
|
166
|
+
|
167
|
+
This class is provided solely for backward compatibility with legacy
|
168
|
+
code that previously relied on the ``Driver`` class. It has been deprecated
|
169
|
+
in favor of the updated abstract base class ``Grid``, which now encompasses
|
170
|
+
all communication-related functionality and improvements between the
|
171
|
+
ServerApp and the SuperLink.
|
172
|
+
|
173
|
+
.. warning::
|
174
|
+
``Driver`` is deprecated and will be removed in a future release.
|
175
|
+
Use `Grid` in the signature of your ServerApp.
|
176
|
+
|
177
|
+
Examples
|
178
|
+
--------
|
179
|
+
Legacy (deprecated) usage::
|
180
|
+
|
181
|
+
@app.main()
|
182
|
+
def main(driver: Driver, context: Context) -> None:
|
183
|
+
...
|
184
|
+
|
185
|
+
Updated usage::
|
186
|
+
|
187
|
+
@app.main()
|
188
|
+
def main(grid: Grid, context: Context) -> None:
|
189
|
+
...
|
190
|
+
"""
|
flwr/server/grid/grpc_grid.py
CHANGED
@@ -12,7 +12,7 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
# ==============================================================================
|
15
|
-
"""Flower gRPC
|
15
|
+
"""Flower gRPC Grid."""
|
16
16
|
|
17
17
|
|
18
18
|
import time
|
@@ -45,11 +45,11 @@ from flwr.proto.serverappio_pb2 import ( # pylint: disable=E0611
|
|
45
45
|
)
|
46
46
|
from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub # pylint: disable=E0611
|
47
47
|
|
48
|
-
from .grid import
|
48
|
+
from .grid import Grid
|
49
49
|
|
50
50
|
ERROR_MESSAGE_PUSH_MESSAGES_RESOURCE_EXHAUSTED = """
|
51
51
|
|
52
|
-
[
|
52
|
+
[Grid.push_messages] gRPC error occurred:
|
53
53
|
|
54
54
|
The 2GB gRPC limit has been reached. Consider reducing the number of messages pushed
|
55
55
|
at once, or push messages individually, for example:
|
@@ -57,13 +57,13 @@ at once, or push messages individually, for example:
|
|
57
57
|
> msgs = [msg1, msg2, msg3]
|
58
58
|
> msg_ids = []
|
59
59
|
> for msg in msgs:
|
60
|
-
> msg_id =
|
60
|
+
> msg_id = grid.push_messages([msg])
|
61
61
|
> msg_ids.extend(msg_id)
|
62
62
|
"""
|
63
63
|
|
64
64
|
ERROR_MESSAGE_PULL_MESSAGES_RESOURCE_EXHAUSTED = """
|
65
65
|
|
66
|
-
[
|
66
|
+
[Grid.pull_messages] gRPC error occurred:
|
67
67
|
|
68
68
|
The 2GB gRPC limit has been reached. Consider reducing the number of messages pulled
|
69
69
|
at once, or pull messages individually, for example:
|
@@ -71,13 +71,13 @@ at once, or pull messages individually, for example:
|
|
71
71
|
> msgs_ids = [msg_id1, msg_id2, msg_id3]
|
72
72
|
> msgs = []
|
73
73
|
> for msg_id in msg_ids:
|
74
|
-
> msg =
|
74
|
+
> msg = grid.pull_messages([msg_id])
|
75
75
|
> msgs.extend(msg)
|
76
76
|
"""
|
77
77
|
|
78
78
|
|
79
|
-
class
|
80
|
-
"""`
|
79
|
+
class GrpcGrid(Grid):
|
80
|
+
"""`GrpcGrid` provides an interface to the ServerAppIo API.
|
81
81
|
|
82
82
|
Parameters
|
83
83
|
----------
|
@@ -101,6 +101,7 @@ class GrpcDriver(Driver):
|
|
101
101
|
self._channel: Optional[grpc.Channel] = None
|
102
102
|
self.node = Node(node_id=SUPERLINK_NODE_ID)
|
103
103
|
self._retry_invoker = _make_simple_grpc_retry_invoker()
|
104
|
+
super().__init__()
|
104
105
|
|
105
106
|
@property
|
106
107
|
def _is_connected(self) -> bool:
|
@@ -197,7 +198,7 @@ class GrpcDriver(Driver):
|
|
197
198
|
|
198
199
|
def get_node_ids(self) -> Iterable[int]:
|
199
200
|
"""Get node IDs."""
|
200
|
-
# Call
|
201
|
+
# Call GrpcServerAppIoStub method
|
201
202
|
res: GetNodesResponse = self._stub.GetNodes(
|
202
203
|
GetNodesRequest(run_id=cast(Run, self._run).run_id)
|
203
204
|
)
|
@@ -220,7 +221,7 @@ class GrpcDriver(Driver):
|
|
220
221
|
message_proto_list.append(msg_proto)
|
221
222
|
|
222
223
|
try:
|
223
|
-
# Call
|
224
|
+
# Call GrpcServerAppIoStub method
|
224
225
|
res: PushInsMessagesResponse = self._stub.PushMessages(
|
225
226
|
PushInsMessagesRequest(
|
226
227
|
messages_list=message_proto_list, run_id=cast(Run, self._run).run_id
|
@@ -12,7 +12,7 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
# ==============================================================================
|
15
|
-
"""Flower in-memory
|
15
|
+
"""Flower in-memory Grid."""
|
16
16
|
|
17
17
|
|
18
18
|
import time
|
@@ -26,16 +26,16 @@ from flwr.common.typing import Run
|
|
26
26
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
27
27
|
from flwr.server.superlink.linkstate import LinkStateFactory
|
28
28
|
|
29
|
-
from .grid import
|
29
|
+
from .grid import Grid
|
30
30
|
|
31
31
|
|
32
|
-
class
|
33
|
-
"""`
|
32
|
+
class InMemoryGrid(Grid):
|
33
|
+
"""`InMemoryGrid` class provides an interface to the ServerAppIo API.
|
34
34
|
|
35
35
|
Parameters
|
36
36
|
----------
|
37
37
|
state_factory : StateFactory
|
38
|
-
A StateFactory embedding a state that this
|
38
|
+
A StateFactory embedding a state that this grid can interface with.
|
39
39
|
pull_interval : float (default=0.1)
|
40
40
|
Sleep duration between calls to `pull_messages`.
|
41
41
|
"""
|
flwr/server/run_serverapp.py
CHANGED
@@ -22,18 +22,18 @@ from flwr.common import Context
|
|
22
22
|
from flwr.common.logger import log
|
23
23
|
from flwr.common.object_ref import load_app
|
24
24
|
|
25
|
-
from .grid import
|
25
|
+
from .grid import Grid
|
26
26
|
from .server_app import LoadServerAppError, ServerApp
|
27
27
|
|
28
28
|
|
29
29
|
def run(
|
30
|
-
|
30
|
+
grid: Grid,
|
31
31
|
context: Context,
|
32
32
|
server_app_dir: str,
|
33
33
|
server_app_attr: Optional[str] = None,
|
34
34
|
loaded_server_app: Optional[ServerApp] = None,
|
35
35
|
) -> Context:
|
36
|
-
"""Run ServerApp with a given
|
36
|
+
"""Run ServerApp with a given Grid."""
|
37
37
|
if not (server_app_attr is None) ^ (loaded_server_app is None):
|
38
38
|
raise ValueError(
|
39
39
|
"Either `server_app_attr` or `loaded_server_app` should be set "
|
@@ -59,7 +59,7 @@ def run(
|
|
59
59
|
server_app = _load()
|
60
60
|
|
61
61
|
# Call ServerApp
|
62
|
-
server_app(
|
62
|
+
server_app(grid=grid, context=context)
|
63
63
|
|
64
64
|
log(DEBUG, "ServerApp finished running.")
|
65
65
|
return context
|
flwr/server/server_app.py
CHANGED
@@ -15,6 +15,7 @@
|
|
15
15
|
"""Flower ServerApp."""
|
16
16
|
|
17
17
|
|
18
|
+
import inspect
|
18
19
|
from collections.abc import Iterator
|
19
20
|
from contextlib import contextmanager
|
20
21
|
from typing import Callable, Optional
|
@@ -24,8 +25,8 @@ from flwr.common.logger import warn_deprecated_feature_with_example
|
|
24
25
|
from flwr.server.strategy import Strategy
|
25
26
|
|
26
27
|
from .client_manager import ClientManager
|
27
|
-
from .compat import
|
28
|
-
from .grid import Driver
|
28
|
+
from .compat import start_grid
|
29
|
+
from .grid import Driver, Grid
|
29
30
|
from .server import Server
|
30
31
|
from .server_config import ServerConfig
|
31
32
|
from .typing import ServerAppCallable, ServerFn
|
@@ -43,6 +44,21 @@ SERVER_FN_USAGE_EXAMPLE = """
|
|
43
44
|
app = ServerApp(server_fn=server_fn)
|
44
45
|
"""
|
45
46
|
|
47
|
+
GRID_USAGE_EXAMPLE = """
|
48
|
+
app = ServerApp()
|
49
|
+
|
50
|
+
@app.main()
|
51
|
+
def main(grid: Grid, context: Context) -> None:
|
52
|
+
# Your existing ServerApp code ...
|
53
|
+
"""
|
54
|
+
|
55
|
+
DRIVER_DEPRECATION_MSG = """
|
56
|
+
The `Driver` class is deprecated, it will be removed in a future release.
|
57
|
+
"""
|
58
|
+
DRIVER_EXAMPLE_MSG = """
|
59
|
+
Instead, use `Grid` in the signature of your `ServerApp`. For example:
|
60
|
+
"""
|
61
|
+
|
46
62
|
|
47
63
|
@contextmanager
|
48
64
|
def _empty_lifespan(_: Context) -> Iterator[None]:
|
@@ -54,7 +70,7 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
54
70
|
|
55
71
|
Examples
|
56
72
|
--------
|
57
|
-
Use the
|
73
|
+
Use the ``ServerApp`` with an existing ``Strategy``:
|
58
74
|
|
59
75
|
>>> def server_fn(context: Context):
|
60
76
|
>>> server_config = ServerConfig(num_rounds=3)
|
@@ -66,12 +82,12 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
66
82
|
>>>
|
67
83
|
>>> app = ServerApp(server_fn=server_fn)
|
68
84
|
|
69
|
-
Use the
|
85
|
+
Use the ``ServerApp`` with a custom main function:
|
70
86
|
|
71
87
|
>>> app = ServerApp()
|
72
88
|
>>>
|
73
89
|
>>> @app.main()
|
74
|
-
>>> def main(
|
90
|
+
>>> def main(grid: Grid, context: Context) -> None:
|
75
91
|
>>> print("ServerApp running")
|
76
92
|
"""
|
77
93
|
|
@@ -111,7 +127,7 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
111
127
|
self._main: Optional[ServerAppCallable] = None
|
112
128
|
self._lifespan = _empty_lifespan
|
113
129
|
|
114
|
-
def __call__(self,
|
130
|
+
def __call__(self, grid: Grid, context: Context) -> None:
|
115
131
|
"""Execute `ServerApp`."""
|
116
132
|
with self._lifespan(context):
|
117
133
|
# Compatibility mode
|
@@ -123,17 +139,17 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
123
139
|
self._config = components.config
|
124
140
|
self._strategy = components.strategy
|
125
141
|
self._client_manager = components.client_manager
|
126
|
-
|
142
|
+
start_grid(
|
127
143
|
server=self._server,
|
128
144
|
config=self._config,
|
129
145
|
strategy=self._strategy,
|
130
146
|
client_manager=self._client_manager,
|
131
|
-
|
147
|
+
grid=grid,
|
132
148
|
)
|
133
149
|
return
|
134
150
|
|
135
151
|
# New execution mode
|
136
|
-
self._main(
|
152
|
+
self._main(grid, context)
|
137
153
|
|
138
154
|
def main(self) -> Callable[[ServerAppCallable], ServerAppCallable]:
|
139
155
|
"""Return a decorator that registers the main fn with the server app.
|
@@ -143,7 +159,7 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
143
159
|
>>> app = ServerApp()
|
144
160
|
>>>
|
145
161
|
>>> @app.main()
|
146
|
-
>>> def main(
|
162
|
+
>>> def main(grid: Grid, context: Context) -> None:
|
147
163
|
>>> print("ServerApp running")
|
148
164
|
"""
|
149
165
|
|
@@ -168,11 +184,21 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
168
184
|
>>> app = ServerApp()
|
169
185
|
>>>
|
170
186
|
>>> @app.main()
|
171
|
-
>>> def main(
|
187
|
+
>>> def main(grid: Grid, context: Context) -> None:
|
172
188
|
>>> print("ServerApp running")
|
173
189
|
""",
|
174
190
|
)
|
175
191
|
|
192
|
+
sig = inspect.signature(main_fn)
|
193
|
+
param = list(sig.parameters.values())[0]
|
194
|
+
# Check if parameter name or the annotation should be updated
|
195
|
+
if param.name == "driver" or param.annotation is Driver:
|
196
|
+
warn_deprecated_feature_with_example(
|
197
|
+
deprecation_message=DRIVER_DEPRECATION_MSG,
|
198
|
+
example_message=DRIVER_EXAMPLE_MSG,
|
199
|
+
code_example=GRID_USAGE_EXAMPLE,
|
200
|
+
)
|
201
|
+
|
176
202
|
# Register provided function with the ServerApp object
|
177
203
|
self._main = main_fn
|
178
204
|
|