flwr-nightly 1.11.0.dev20240822__py3-none-any.whl → 1.11.0.dev20240823__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.

Potentially problematic release.


This version of flwr-nightly might be problematic. Click here for more details.

flwr/cli/build.py CHANGED
@@ -89,7 +89,7 @@ def build(
89
89
  # Set the name of the zip file
90
90
  fab_filename = (
91
91
  f"{conf['tool']['flwr']['app']['publisher']}"
92
- f".{app.name}"
92
+ f".{conf['project']['name']}"
93
93
  f".{conf['project']['version'].replace('.', '-')}.fab"
94
94
  )
95
95
  list_file_content = ""
@@ -1,43 +1,20 @@
1
1
  # $project_name: A Flower / $framework_str app
2
2
 
3
- ## Install dependencies
3
+ ## Install dependencies and project
4
4
 
5
5
  ```bash
6
- pip install .
6
+ pip install -e .
7
7
  ```
8
8
 
9
- ## Run (Simulation Engine)
9
+ ## Run with the Simulation Engine
10
10
 
11
11
  In the `$project_name` directory, use `flwr run` to run a local simulation:
12
12
 
13
13
  ```bash
14
- flwr run
14
+ flwr run .
15
15
  ```
16
16
 
17
- ## Run (Deployment Engine)
17
+ ## Run with the Deployment Engine
18
18
 
19
- ### Start the SuperLink
20
-
21
- ```bash
22
- flower-superlink --insecure
23
- ```
24
-
25
- ### Start the long-running Flower client
26
-
27
- In a new terminal window, start the first long-running Flower client:
28
-
29
- ```bash
30
- flower-client-app client:app --insecure
31
- ```
32
-
33
- In yet another new terminal window, start the second long-running Flower client:
34
-
35
- ```bash
36
- flower-client-app client:app --insecure
37
- ```
38
-
39
- ### Start the ServerApp
40
-
41
- ```bash
42
- flower-server-app server:app --insecure
43
- ```
19
+ > \[!NOTE\]
20
+ > An update to this example will show how to run this Flower application with the Deployment Engine and TLS certificates, or with Docker.
flwr/cli/run/run.py CHANGED
@@ -15,6 +15,7 @@
15
15
  """Flower command line interface `run` command."""
16
16
 
17
17
  import hashlib
18
+ import json
18
19
  import subprocess
19
20
  import sys
20
21
  from logging import DEBUG
@@ -192,6 +193,8 @@ def _run_without_superexec(
192
193
  ) -> None:
193
194
  try:
194
195
  num_supernodes = federation_config["options"]["num-supernodes"]
196
+ verbose: Optional[bool] = federation_config["options"].get("verbose")
197
+ backend_cfg = federation_config["options"].get("backend", {})
195
198
  except KeyError as err:
196
199
  typer.secho(
197
200
  "❌ The project's `pyproject.toml` needs to declare the number of"
@@ -212,6 +215,13 @@ def _run_without_superexec(
212
215
  f"{num_supernodes}",
213
216
  ]
214
217
 
218
+ if backend_cfg:
219
+ # Stringify as JSON
220
+ command.extend(["--backend-config", json.dumps(backend_cfg)])
221
+
222
+ if verbose:
223
+ command.extend(["--verbose"])
224
+
215
225
  if config_overrides:
216
226
  command.extend(["--run-config", f"{' '.join(config_overrides)}"])
217
227
 
flwr/client/client.py CHANGED
@@ -33,12 +33,13 @@ from flwr.common import (
33
33
  Parameters,
34
34
  Status,
35
35
  )
36
+ from flwr.common.logger import warn_deprecated_feature_with_example
36
37
 
37
38
 
38
39
  class Client(ABC):
39
40
  """Abstract base class for Flower clients."""
40
41
 
41
- context: Context
42
+ _context: Context
42
43
 
43
44
  def get_properties(self, ins: GetPropertiesIns) -> GetPropertiesRes:
44
45
  """Return set of client's properties.
@@ -141,6 +142,26 @@ class Client(ABC):
141
142
  metrics={},
142
143
  )
143
144
 
145
+ @property
146
+ def context(self) -> Context:
147
+ """Getter for `Context` client attribute."""
148
+ warn_deprecated_feature_with_example(
149
+ "Accessing the context via the client's attribute is deprecated.",
150
+ example_message="Instead, pass it to the client's "
151
+ "constructor in your `client_fn()` which already "
152
+ "receives a context object.",
153
+ code_example="def client_fn(context: Context) -> Client:\n\n"
154
+ "\t\t# Your existing client_fn\n\n"
155
+ "\t\t# Pass `context` to the constructor\n"
156
+ "\t\treturn FlowerClient(context).to_client()",
157
+ )
158
+ return self._context
159
+
160
+ @context.setter
161
+ def context(self, context: Context) -> None:
162
+ """Setter for `Context` client attribute."""
163
+ self._context = context
164
+
144
165
  def get_context(self) -> Context:
145
166
  """Get the run context from this client."""
146
167
  return self.context
@@ -27,6 +27,7 @@ from flwr.common import (
27
27
  ndarrays_to_parameters,
28
28
  parameters_to_ndarrays,
29
29
  )
30
+ from flwr.common.logger import warn_deprecated_feature_with_example
30
31
  from flwr.common.typing import (
31
32
  Code,
32
33
  EvaluateIns,
@@ -70,7 +71,7 @@ Example
70
71
  class NumPyClient(ABC):
71
72
  """Abstract base class for Flower clients using NumPy."""
72
73
 
73
- context: Context
74
+ _context: Context
74
75
 
75
76
  def get_properties(self, config: Config) -> Dict[str, Scalar]:
76
77
  """Return a client's set of properties.
@@ -174,6 +175,26 @@ class NumPyClient(ABC):
174
175
  _ = (self, parameters, config)
175
176
  return 0.0, 0, {}
176
177
 
178
+ @property
179
+ def context(self) -> Context:
180
+ """Getter for `Context` client attribute."""
181
+ warn_deprecated_feature_with_example(
182
+ "Accessing the context via the client's attribute is deprecated.",
183
+ example_message="Instead, pass it to the client's "
184
+ "constructor in your `client_fn()` which already "
185
+ "receives a context object.",
186
+ code_example="def client_fn(context: Context) -> Client:\n\n"
187
+ "\t\t# Your existing client_fn\n\n"
188
+ "\t\t# Pass `context` to the constructor\n"
189
+ "\t\treturn FlowerClient(context).to_client()",
190
+ )
191
+ return self._context
192
+
193
+ @context.setter
194
+ def context(self, context: Context) -> None:
195
+ """Setter for `Context` client attribute."""
196
+ self._context = context
197
+
177
198
  def get_context(self) -> Context:
178
199
  """Get the run context from this client."""
179
200
  return self.context
@@ -275,7 +275,7 @@ def http_request_response( # pylint: disable=,R0913, R0914, R0915
275
275
  req = DeleteNodeRequest(node=node)
276
276
 
277
277
  # Send the request
278
- res = _request(req, DeleteNodeResponse, PATH_CREATE_NODE)
278
+ res = _request(req, DeleteNodeResponse, PATH_DELETE_NODE)
279
279
  if res is None:
280
280
  return
281
281
 
flwr/common/address.py CHANGED
@@ -14,6 +14,7 @@
14
14
  # ==============================================================================
15
15
  """Flower IP address utils."""
16
16
 
17
+ import socket
17
18
  from ipaddress import ip_address
18
19
  from typing import Optional, Tuple
19
20
 
@@ -57,3 +58,45 @@ def parse_address(address: str) -> Optional[Tuple[str, int, Optional[bool]]]:
57
58
 
58
59
  except ValueError:
59
60
  return None
61
+
62
+
63
+ def is_port_in_use(address: str) -> bool:
64
+ """Check if the port specified in address is in use.
65
+
66
+ Parameters
67
+ ----------
68
+ address : str
69
+ The string representation of a domain, an IPv4, or an IPV6 address
70
+ with the port number.
71
+
72
+ For example, '127.0.0.1:8080', or `[::1]:8080`.
73
+
74
+ Returns
75
+ -------
76
+ bool
77
+ If the port provided is in use or can't be parsed,
78
+ the function will return True, otherwise it will return False.
79
+ """
80
+ parsed_address = parse_address(address)
81
+ if not parsed_address:
82
+ return True
83
+ host, port, is_v6 = parsed_address
84
+
85
+ if is_v6:
86
+ protocol = socket.AF_INET6
87
+ else:
88
+ protocol = socket.AF_INET
89
+
90
+ with socket.socket(protocol, socket.SOCK_STREAM) as s:
91
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
92
+ try:
93
+ if is_v6:
94
+ # For IPv6, provide `flowinfo` and `scopeid` as 0
95
+ s.bind((host, port, 0, 0))
96
+ else:
97
+ # For IPv4
98
+ s.bind((host, port))
99
+ except OSError:
100
+ return True
101
+
102
+ return False
flwr/server/app.py CHANGED
@@ -271,6 +271,7 @@ def run_superlink() -> None:
271
271
  ssl_keyfile,
272
272
  ssl_certfile,
273
273
  state_factory,
274
+ ffs_factory,
274
275
  num_workers,
275
276
  ),
276
277
  )
@@ -310,6 +311,7 @@ def run_superlink() -> None:
310
311
  fleet_server = _run_fleet_api_grpc_adapter(
311
312
  address=fleet_address,
312
313
  state_factory=state_factory,
314
+ ffs_factory=ffs_factory,
313
315
  certificates=certificates,
314
316
  )
315
317
  grpc_servers.append(fleet_server)
@@ -516,12 +518,14 @@ def _run_fleet_api_grpc_rere(
516
518
  def _run_fleet_api_grpc_adapter(
517
519
  address: str,
518
520
  state_factory: StateFactory,
521
+ ffs_factory: FfsFactory,
519
522
  certificates: Optional[Tuple[bytes, bytes, bytes]],
520
523
  ) -> grpc.Server:
521
524
  """Run Fleet API (GrpcAdapter)."""
522
525
  # Create Fleet API gRPC server
523
526
  fleet_servicer = GrpcAdapterServicer(
524
527
  state_factory=state_factory,
528
+ ffs_factory=ffs_factory,
525
529
  )
526
530
  fleet_add_servicer_to_server_fn = add_GrpcAdapterServicer_to_server
527
531
  fleet_grpc_server = generic_create_grpc_server(
@@ -544,6 +548,7 @@ def _run_fleet_api_rest(
544
548
  ssl_keyfile: Optional[str],
545
549
  ssl_certfile: Optional[str],
546
550
  state_factory: StateFactory,
551
+ ffs_factory: FfsFactory,
547
552
  num_workers: int,
548
553
  ) -> None:
549
554
  """Run Driver API (REST-based)."""
@@ -558,6 +563,7 @@ def _run_fleet_api_rest(
558
563
 
559
564
  # See: https://www.starlette.io/applications/#accessing-the-app-instance
560
565
  fast_api_app.state.STATE_FACTORY = state_factory
566
+ fast_api_app.state.FFS_FACTORY = ffs_factory
561
567
 
562
568
  uvicorn.run(
563
569
  app="flwr.server.superlink.fleet.rest_rere.rest_api:app",
@@ -23,6 +23,7 @@ from google.protobuf.message import Message as GrpcMessage
23
23
 
24
24
  from flwr.common.logger import log
25
25
  from flwr.proto import grpcadapter_pb2_grpc # pylint: disable=E0611
26
+ from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
26
27
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
27
28
  CreateNodeRequest,
28
29
  CreateNodeResponse,
@@ -37,6 +38,7 @@ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
37
38
  )
38
39
  from flwr.proto.grpcadapter_pb2 import MessageContainer # pylint: disable=E0611
39
40
  from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
41
+ from flwr.server.superlink.ffs.ffs_factory import FfsFactory
40
42
  from flwr.server.superlink.fleet.message_handler import message_handler
41
43
  from flwr.server.superlink.state import StateFactory
42
44
 
@@ -60,10 +62,11 @@ def _handle(
60
62
  class GrpcAdapterServicer(grpcadapter_pb2_grpc.GrpcAdapterServicer):
61
63
  """Fleet API via GrpcAdapter servicer."""
62
64
 
63
- def __init__(self, state_factory: StateFactory) -> None:
65
+ def __init__(self, state_factory: StateFactory, ffs_factory: FfsFactory) -> None:
64
66
  self.state_factory = state_factory
67
+ self.ffs_factory = ffs_factory
65
68
 
66
- def SendReceive(
69
+ def SendReceive( # pylint: disable=too-many-return-statements
67
70
  self, request: MessageContainer, context: grpc.ServicerContext
68
71
  ) -> MessageContainer:
69
72
  """."""
@@ -80,6 +83,8 @@ class GrpcAdapterServicer(grpcadapter_pb2_grpc.GrpcAdapterServicer):
80
83
  return _handle(request, PushTaskResRequest, self._push_task_res)
81
84
  if request.grpc_message_name == GetRunRequest.__qualname__:
82
85
  return _handle(request, GetRunRequest, self._get_run)
86
+ if request.grpc_message_name == GetFabRequest.__qualname__:
87
+ return _handle(request, GetFabRequest, self._get_fab)
83
88
  raise ValueError(f"Invalid grpc_message_name: {request.grpc_message_name}")
84
89
 
85
90
  def _create_node(self, request: CreateNodeRequest) -> CreateNodeResponse:
@@ -129,3 +134,11 @@ class GrpcAdapterServicer(grpcadapter_pb2_grpc.GrpcAdapterServicer):
129
134
  request=request,
130
135
  state=self.state_factory.state(),
131
136
  )
137
+
138
+ def _get_fab(self, request: GetFabRequest) -> GetFabResponse:
139
+ """Get FAB."""
140
+ log(INFO, "GrpcAdapter.GetFab")
141
+ return message_handler.get_fab(
142
+ request=request,
143
+ ffs=self.ffs_factory.ffs(),
144
+ )
@@ -23,6 +23,7 @@ from typing import Any, Callable, Optional, Sequence, Tuple, Union
23
23
  import grpc
24
24
 
25
25
  from flwr.common import GRPC_MAX_MESSAGE_LENGTH
26
+ from flwr.common.address import is_port_in_use
26
27
  from flwr.common.logger import log
27
28
  from flwr.proto.transport_pb2_grpc import ( # pylint: disable=E0611
28
29
  add_FlowerServiceServicer_to_server,
@@ -218,6 +219,10 @@ def generic_create_grpc_server( # pylint: disable=too-many-arguments
218
219
  server : grpc.Server
219
220
  A non-running instance of a gRPC server.
220
221
  """
222
+ # Check if port is in use
223
+ if is_port_in_use(server_address):
224
+ sys.exit(f"Port in server address {server_address} is already in use.")
225
+
221
226
  # Deconstruct tuple into servicer and function
222
227
  servicer, add_servicer_to_server_fn = servicer_and_add_fn
223
228
 
@@ -15,17 +15,29 @@
15
15
  """Experimental REST API server."""
16
16
 
17
17
 
18
+ from __future__ import annotations
19
+
18
20
  import sys
21
+ from typing import Awaitable, Callable, TypeVar
22
+
23
+ from google.protobuf.message import Message as GrpcMessage
19
24
 
20
25
  from flwr.common.constant import MISSING_EXTRA_REST
26
+ from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
21
27
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
22
28
  CreateNodeRequest,
29
+ CreateNodeResponse,
23
30
  DeleteNodeRequest,
31
+ DeleteNodeResponse,
24
32
  PingRequest,
33
+ PingResponse,
25
34
  PullTaskInsRequest,
35
+ PullTaskInsResponse,
26
36
  PushTaskResRequest,
37
+ PushTaskResResponse,
27
38
  )
28
- from flwr.proto.run_pb2 import GetRunRequest # pylint: disable=E0611
39
+ from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
40
+ from flwr.server.superlink.ffs.ffs import Ffs
29
41
  from flwr.server.superlink.fleet.message_handler import message_handler
30
42
  from flwr.server.superlink.state import State
31
43
 
@@ -40,172 +52,108 @@ except ModuleNotFoundError:
40
52
  sys.exit(MISSING_EXTRA_REST)
41
53
 
42
54
 
43
- async def create_node(request: Request) -> Response:
44
- """Create Node."""
45
- _check_headers(request.headers)
55
+ GrpcRequest = TypeVar("GrpcRequest", bound=GrpcMessage)
56
+ GrpcResponse = TypeVar("GrpcResponse", bound=GrpcMessage)
46
57
 
47
- # Get the request body as raw bytes
48
- create_node_request_bytes: bytes = await request.body()
58
+ GrpcAsyncFunction = Callable[[GrpcRequest], Awaitable[GrpcResponse]]
59
+ RestEndPoint = Callable[[Request], Awaitable[Response]]
49
60
 
50
- # Deserialize ProtoBuf
51
- create_node_request_proto = CreateNodeRequest()
52
- create_node_request_proto.ParseFromString(create_node_request_bytes)
53
61
 
54
- # Get state from app
55
- state: State = app.state.STATE_FACTORY.state()
62
+ def rest_request_response(
63
+ grpc_request_type: type[GrpcRequest],
64
+ ) -> Callable[[GrpcAsyncFunction[GrpcRequest, GrpcResponse]], RestEndPoint]:
65
+ """Convert an async gRPC-based function into a RESTful HTTP endpoint."""
56
66
 
57
- # Handle message
58
- create_node_response_proto = message_handler.create_node(
59
- request=create_node_request_proto, state=state
60
- )
67
+ def decorator(func: GrpcAsyncFunction[GrpcRequest, GrpcResponse]) -> RestEndPoint:
68
+ async def wrapper(request: Request) -> Response:
69
+ _check_headers(request.headers)
61
70
 
62
- # Return serialized ProtoBuf
63
- create_node_response_bytes = create_node_response_proto.SerializeToString()
64
- return Response(
65
- status_code=200,
66
- content=create_node_response_bytes,
67
- headers={"Content-Type": "application/protobuf"},
68
- )
71
+ # Get the request body as raw bytes
72
+ grpc_req_bytes: bytes = await request.body()
69
73
 
74
+ # Deserialize ProtoBuf
75
+ grpc_req = grpc_request_type.FromString(grpc_req_bytes)
76
+ grpc_res = await func(grpc_req)
77
+ return Response(
78
+ status_code=200,
79
+ content=grpc_res.SerializeToString(),
80
+ headers={"Content-Type": "application/protobuf"},
81
+ )
70
82
 
71
- async def delete_node(request: Request) -> Response:
72
- """Delete Node Id."""
73
- _check_headers(request.headers)
83
+ return wrapper
74
84
 
75
- # Get the request body as raw bytes
76
- delete_node_request_bytes: bytes = await request.body()
85
+ return decorator
77
86
 
78
- # Deserialize ProtoBuf
79
- delete_node_request_proto = DeleteNodeRequest()
80
- delete_node_request_proto.ParseFromString(delete_node_request_bytes)
81
87
 
88
+ @rest_request_response(CreateNodeRequest)
89
+ async def create_node(request: CreateNodeRequest) -> CreateNodeResponse:
90
+ """Create Node."""
82
91
  # Get state from app
83
92
  state: State = app.state.STATE_FACTORY.state()
84
93
 
85
94
  # Handle message
86
- delete_node_response_proto = message_handler.delete_node(
87
- request=delete_node_request_proto, state=state
88
- )
95
+ return message_handler.create_node(request=request, state=state)
89
96
 
90
- # Return serialized ProtoBuf
91
- delete_node_response_bytes = delete_node_response_proto.SerializeToString()
92
- return Response(
93
- status_code=200,
94
- content=delete_node_response_bytes,
95
- headers={"Content-Type": "application/protobuf"},
96
- )
97
97
 
98
+ @rest_request_response(DeleteNodeRequest)
99
+ async def delete_node(request: DeleteNodeRequest) -> DeleteNodeResponse:
100
+ """Delete Node Id."""
101
+ # Get state from app
102
+ state: State = app.state.STATE_FACTORY.state()
98
103
 
99
- async def pull_task_ins(request: Request) -> Response:
100
- """Pull TaskIns."""
101
- _check_headers(request.headers)
102
-
103
- # Get the request body as raw bytes
104
- pull_task_ins_request_bytes: bytes = await request.body()
104
+ # Handle message
105
+ return message_handler.delete_node(request=request, state=state)
105
106
 
106
- # Deserialize ProtoBuf
107
- pull_task_ins_request_proto = PullTaskInsRequest()
108
- pull_task_ins_request_proto.ParseFromString(pull_task_ins_request_bytes)
109
107
 
108
+ @rest_request_response(PullTaskInsRequest)
109
+ async def pull_task_ins(request: PullTaskInsRequest) -> PullTaskInsResponse:
110
+ """Pull TaskIns."""
110
111
  # Get state from app
111
112
  state: State = app.state.STATE_FACTORY.state()
112
113
 
113
114
  # Handle message
114
- pull_task_ins_response_proto = message_handler.pull_task_ins(
115
- request=pull_task_ins_request_proto,
116
- state=state,
117
- )
118
-
119
- # Return serialized ProtoBuf
120
- pull_task_ins_response_bytes = pull_task_ins_response_proto.SerializeToString()
121
- return Response(
122
- status_code=200,
123
- content=pull_task_ins_response_bytes,
124
- headers={"Content-Type": "application/protobuf"},
125
- )
115
+ return message_handler.pull_task_ins(request=request, state=state)
126
116
 
127
117
 
128
- async def push_task_res(request: Request) -> Response: # Check if token is needed here
118
+ # Check if token is needed here
119
+ @rest_request_response(PushTaskResRequest)
120
+ async def push_task_res(request: PushTaskResRequest) -> PushTaskResResponse:
129
121
  """Push TaskRes."""
130
- _check_headers(request.headers)
131
-
132
- # Get the request body as raw bytes
133
- push_task_res_request_bytes: bytes = await request.body()
134
-
135
- # Deserialize ProtoBuf
136
- push_task_res_request_proto = PushTaskResRequest()
137
- push_task_res_request_proto.ParseFromString(push_task_res_request_bytes)
138
-
139
122
  # Get state from app
140
123
  state: State = app.state.STATE_FACTORY.state()
141
124
 
142
125
  # Handle message
143
- push_task_res_response_proto = message_handler.push_task_res(
144
- request=push_task_res_request_proto,
145
- state=state,
146
- )
147
-
148
- # Return serialized ProtoBuf
149
- push_task_res_response_bytes = push_task_res_response_proto.SerializeToString()
150
- return Response(
151
- status_code=200,
152
- content=push_task_res_response_bytes,
153
- headers={"Content-Type": "application/protobuf"},
154
- )
126
+ return message_handler.push_task_res(request=request, state=state)
155
127
 
156
128
 
157
- async def ping(request: Request) -> Response:
129
+ @rest_request_response(PingRequest)
130
+ async def ping(request: PingRequest) -> PingResponse:
158
131
  """Ping."""
159
- _check_headers(request.headers)
160
-
161
- # Get the request body as raw bytes
162
- ping_request_bytes: bytes = await request.body()
163
-
164
- # Deserialize ProtoBuf
165
- ping_request_proto = PingRequest()
166
- ping_request_proto.ParseFromString(ping_request_bytes)
167
-
168
132
  # Get state from app
169
133
  state: State = app.state.STATE_FACTORY.state()
170
134
 
171
135
  # Handle message
172
- ping_response_proto = message_handler.ping(request=ping_request_proto, state=state)
173
-
174
- # Return serialized ProtoBuf
175
- ping_response_bytes = ping_response_proto.SerializeToString()
176
- return Response(
177
- status_code=200,
178
- content=ping_response_bytes,
179
- headers={"Content-Type": "application/protobuf"},
180
- )
136
+ return message_handler.ping(request=request, state=state)
181
137
 
182
138
 
183
- async def get_run(request: Request) -> Response:
139
+ @rest_request_response(GetRunRequest)
140
+ async def get_run(request: GetRunRequest) -> GetRunResponse:
184
141
  """GetRun."""
185
- _check_headers(request.headers)
186
-
187
- # Get the request body as raw bytes
188
- get_run_request_bytes: bytes = await request.body()
189
-
190
- # Deserialize ProtoBuf
191
- get_run_request_proto = GetRunRequest()
192
- get_run_request_proto.ParseFromString(get_run_request_bytes)
193
-
194
142
  # Get state from app
195
143
  state: State = app.state.STATE_FACTORY.state()
196
144
 
197
145
  # Handle message
198
- get_run_response_proto = message_handler.get_run(
199
- request=get_run_request_proto, state=state
200
- )
146
+ return message_handler.get_run(request=request, state=state)
147
+
201
148
 
202
- # Return serialized ProtoBuf
203
- get_run_response_bytes = get_run_response_proto.SerializeToString()
204
- return Response(
205
- status_code=200,
206
- content=get_run_response_bytes,
207
- headers={"Content-Type": "application/protobuf"},
208
- )
149
+ @rest_request_response(GetFabRequest)
150
+ async def get_fab(request: GetFabRequest) -> GetFabResponse:
151
+ """GetRun."""
152
+ # Get ffs from app
153
+ ffs: Ffs = app.state.FFS_FACTORY.state()
154
+
155
+ # Handle message
156
+ return message_handler.get_fab(request=request, ffs=ffs)
209
157
 
210
158
 
211
159
  routes = [
@@ -215,6 +163,7 @@ routes = [
215
163
  Route("/api/v0/fleet/push-task-res", push_task_res, methods=["POST"]),
216
164
  Route("/api/v0/fleet/ping", ping, methods=["POST"]),
217
165
  Route("/api/v0/fleet/get-run", get_run, methods=["POST"]),
166
+ Route("/api/v0/fleet/get-fab", get_fab, methods=["POST"]),
218
167
  ]
219
168
 
220
169
  app: Starlette = Starlette(
@@ -33,7 +33,7 @@ class Backend(ABC):
33
33
  """Construct a backend."""
34
34
 
35
35
  @abstractmethod
36
- def build(self) -> None:
36
+ def build(self, app_fn: Callable[[], ClientApp]) -> None:
37
37
  """Build backend.
38
38
 
39
39
  Different components need to be in place before workers in a backend are ready
@@ -60,7 +60,6 @@ class Backend(ABC):
60
60
  @abstractmethod
61
61
  def process_message(
62
62
  self,
63
- app: Callable[[], ClientApp],
64
63
  message: Message,
65
64
  context: Context,
66
65
  ) -> Tuple[Message, Context]:
@@ -16,7 +16,7 @@
16
16
 
17
17
  import sys
18
18
  from logging import DEBUG, ERROR
19
- from typing import Callable, Dict, Tuple, Union
19
+ from typing import Callable, Dict, Optional, Tuple, Union
20
20
 
21
21
  import ray
22
22
 
@@ -63,6 +63,8 @@ class RayBackend(Backend):
63
63
  actor_kwargs=actor_kwargs,
64
64
  )
65
65
 
66
+ self.app_fn: Optional[Callable[[], ClientApp]] = None
67
+
66
68
  def _validate_client_resources(self, config: BackendConfig) -> ClientResourcesDict:
67
69
  client_resources_config = config.get(self.client_resources_key)
68
70
  client_resources: ClientResourcesDict = {}
@@ -126,14 +128,15 @@ class RayBackend(Backend):
126
128
  """Report whether the pool has idle actors."""
127
129
  return self.pool.is_actor_available()
128
130
 
129
- def build(self) -> None:
131
+ def build(self, app_fn: Callable[[], ClientApp]) -> None:
130
132
  """Build pool of Ray actors that this backend will submit jobs to."""
131
133
  self.pool.add_actors_to_pool(self.pool.actors_capacity)
134
+ # Set ClientApp callable that ray actors will use
135
+ self.app_fn = app_fn
132
136
  log(DEBUG, "Constructed ActorPool with: %i actors", self.pool.num_actors)
133
137
 
134
138
  def process_message(
135
139
  self,
136
- app: Callable[[], ClientApp],
137
140
  message: Message,
138
141
  context: Context,
139
142
  ) -> Tuple[Message, Context]:
@@ -143,11 +146,17 @@ class RayBackend(Backend):
143
146
  """
144
147
  partition_id = context.node_config[PARTITION_ID_KEY]
145
148
 
149
+ if self.app_fn is None:
150
+ raise ValueError(
151
+ "Unspecified function to load a `ClientApp`. "
152
+ "Call the backend's `build()` method before processing messages."
153
+ )
154
+
146
155
  try:
147
156
  # Submit a task to the pool
148
157
  future = self.pool.submit(
149
158
  lambda a, a_fn, mssg, cid, state: a.run.remote(a_fn, mssg, cid, state),
150
- (app, message, str(partition_id), context),
159
+ (self.app_fn, message, str(partition_id), context),
151
160
  )
152
161
 
153
162
  # Fetch result
@@ -87,7 +87,6 @@ def _register_node_states(
87
87
 
88
88
  # pylint: disable=too-many-arguments,too-many-locals
89
89
  def worker(
90
- app_fn: Callable[[], ClientApp],
91
90
  taskins_queue: "Queue[TaskIns]",
92
91
  taskres_queue: "Queue[TaskRes]",
93
92
  node_states: Dict[int, NodeState],
@@ -110,9 +109,7 @@ def worker(
110
109
  message = message_from_taskins(task_ins)
111
110
 
112
111
  # Let backend process message
113
- out_mssg, updated_context = backend.process_message(
114
- app_fn, message, context
115
- )
112
+ out_mssg, updated_context = backend.process_message(message, context)
116
113
 
117
114
  # Update Context
118
115
  node_states[node_id].update_context(
@@ -193,7 +190,7 @@ def run_api(
193
190
  backend = backend_fn()
194
191
 
195
192
  # Build backend
196
- backend.build()
193
+ backend.build(app_fn)
197
194
 
198
195
  # Add workers (they submit Messages to Backend)
199
196
  state = state_factory.state()
@@ -223,7 +220,6 @@ def run_api(
223
220
  _ = [
224
221
  executor.submit(
225
222
  worker,
226
- app_fn,
227
223
  taskins_queue,
228
224
  taskres_queue,
229
225
  node_states,
@@ -35,6 +35,7 @@ class SecAggWorkflow(SecAggPlusWorkflow):
35
35
  contributions to compute the weighted average of model parameters.
36
36
 
37
37
  The protocol involves four main stages:
38
+
38
39
  - 'setup': Send SecAgg configuration to clients and collect their public keys.
39
40
  - 'share keys': Broadcast public keys among clients and collect encrypted secret
40
41
  key shares.
@@ -99,6 +99,7 @@ class SecAggPlusWorkflow:
99
99
  contributions to compute the weighted average of model parameters.
100
100
 
101
101
  The protocol involves four main stages:
102
+
102
103
  - 'setup': Send SecAgg+ configuration to clients and collect their public keys.
103
104
  - 'share keys': Broadcast public keys among clients and collect encrypted secret
104
105
  key shares.
@@ -25,7 +25,7 @@ from argparse import Namespace
25
25
  from logging import DEBUG, ERROR, INFO, WARNING
26
26
  from pathlib import Path
27
27
  from time import sleep
28
- from typing import List, Optional
28
+ from typing import Any, List, Optional
29
29
 
30
30
  from flwr.cli.config_utils import load_and_validate
31
31
  from flwr.client import ClientApp
@@ -35,6 +35,7 @@ from flwr.common.constant import RUN_ID_NUM_BYTES
35
35
  from flwr.common.logger import (
36
36
  set_logger_propagation,
37
37
  update_console_handler,
38
+ warn_deprecated_feature,
38
39
  warn_deprecated_feature_with_example,
39
40
  )
40
41
  from flwr.common.typing import Run, UserConfig
@@ -91,12 +92,36 @@ def _check_args_do_not_interfere(args: Namespace) -> bool:
91
92
  return True
92
93
 
93
94
 
95
+ def _replace_keys(d: Any, match: str, target: str) -> Any:
96
+ if isinstance(d, dict):
97
+ return {
98
+ k.replace(match, target): _replace_keys(v, match, target)
99
+ for k, v in d.items()
100
+ }
101
+ if isinstance(d, list):
102
+ return [_replace_keys(i, match, target) for i in d]
103
+ return d
104
+
105
+
94
106
  # Entry point from CLI
95
107
  # pylint: disable=too-many-locals
96
108
  def run_simulation_from_cli() -> None:
97
109
  """Run Simulation Engine from the CLI."""
98
110
  args = _parse_args_run_simulation().parse_args()
99
111
 
112
+ # Add warnings for deprecated server_app and client_app arguments
113
+ if args.server_app:
114
+ warn_deprecated_feature(
115
+ "The `--server-app` argument is deprecated. "
116
+ "Please use the `--app` argument instead."
117
+ )
118
+
119
+ if args.client_app:
120
+ warn_deprecated_feature(
121
+ "The `--client-app` argument is deprecated. "
122
+ "Use the `--app` argument instead."
123
+ )
124
+
100
125
  if args.enable_tf_gpu_growth:
101
126
  warn_deprecated_feature_with_example(
102
127
  "Passing `--enable-tf-gpu-growth` is deprecated.",
@@ -105,6 +130,14 @@ def run_simulation_from_cli() -> None:
105
130
  code_example='TF_FORCE_GPU_ALLOW_GROWTH="true" flower-simulation <...>',
106
131
  )
107
132
 
133
+ # Load JSON config
134
+ backend_config_dict = json.loads(args.backend_config)
135
+
136
+ if backend_config_dict:
137
+ # Backend config internally operates with `_` not with `-`
138
+ backend_config_dict = _replace_keys(backend_config_dict, match="-", target="_")
139
+ log(DEBUG, "backend_config_dict: %s", backend_config_dict)
140
+
108
141
  # We are supporting two modes for the CLI entrypoint:
109
142
  # 1) Running an app dir containing a `pyproject.toml`
110
143
  # 2) Running any ClientApp and SeverApp w/o pyproject.toml being present
@@ -167,9 +200,6 @@ def run_simulation_from_cli() -> None:
167
200
  override_config=override_config,
168
201
  )
169
202
 
170
- # Load JSON config
171
- backend_config_dict = json.loads(args.backend_config)
172
-
173
203
  _run_simulation(
174
204
  server_app_attr=server_app_attr,
175
205
  client_app_attr=client_app_attr,
@@ -209,9 +239,8 @@ def run_simulation(
209
239
  messages sent by the `ServerApp`.
210
240
 
211
241
  num_supernodes : int
212
- Number of nodes that run a ClientApp. They can be sampled by a
213
- Driver in the ServerApp and receive a Message describing what the ClientApp
214
- should perform.
242
+ Number of nodes that run a ClientApp. They can be sampled by a Driver in the
243
+ ServerApp and receive a Message describing what the ClientApp should perform.
215
244
 
216
245
  backend_name : str (default: ray)
217
246
  A simulation backend that runs `ClientApp`s.
@@ -239,7 +268,7 @@ def run_simulation(
239
268
  if enable_tf_gpu_growth:
240
269
  warn_deprecated_feature_with_example(
241
270
  "Passing `enable_tf_gpu_growth=True` is deprecated.",
242
- example_message="Instead, set the `TF_FORCE_GPU_ALLOW_GROWTH` environmnet "
271
+ example_message="Instead, set the `TF_FORCE_GPU_ALLOW_GROWTH` environment "
243
272
  "variable to true.",
244
273
  code_example='import os;os.environ["TF_FORCE_GPU_ALLOW_GROWTH"]="true"'
245
274
  "\n\tflwr.simulation.run_simulationt(...)",
@@ -512,13 +541,22 @@ def _parse_args_run_simulation() -> argparse.ArgumentParser:
512
541
  parser = argparse.ArgumentParser(
513
542
  description="Start a Flower simulation",
514
543
  )
544
+ parser.add_argument(
545
+ "--app",
546
+ type=str,
547
+ default=None,
548
+ help="Path to a directory containing a FAB-like structure with a "
549
+ "pyproject.toml.",
550
+ )
515
551
  parser.add_argument(
516
552
  "--server-app",
517
- help="For example: `server:app` or `project.package.module:wrapper.app`",
553
+ help="(DEPRECATED: use --app instead) For example: `server:app` or "
554
+ "`project.package.module:wrapper.app`",
518
555
  )
519
556
  parser.add_argument(
520
557
  "--client-app",
521
- help="For example: `client:app` or `project.package.module:wrapper.app`",
558
+ help="(DEPRECATED: use --app instead) For example: `client:app` or "
559
+ "`project.package.module:wrapper.app`",
522
560
  )
523
561
  parser.add_argument(
524
562
  "--num-supernodes",
@@ -526,13 +564,6 @@ def _parse_args_run_simulation() -> argparse.ArgumentParser:
526
564
  required=True,
527
565
  help="Number of simulated SuperNodes.",
528
566
  )
529
- parser.add_argument(
530
- "--app",
531
- type=str,
532
- default=None,
533
- help="Path to a directory containing a FAB-like structure with a "
534
- "pyproject.toml.",
535
- )
536
567
  parser.add_argument(
537
568
  "--run-config",
538
569
  default=None,
@@ -15,6 +15,7 @@
15
15
  """Simulation engine executor."""
16
16
 
17
17
 
18
+ import json
18
19
  import subprocess
19
20
  import sys
20
21
  from logging import ERROR, INFO, WARN
@@ -24,6 +25,7 @@ from typing_extensions import override
24
25
 
25
26
  from flwr.cli.config_utils import load_and_validate
26
27
  from flwr.cli.install import install_from_fab
28
+ from flwr.common.config import unflatten_dict
27
29
  from flwr.common.constant import RUN_ID_NUM_BYTES
28
30
  from flwr.common.logger import log
29
31
  from flwr.common.typing import UserConfig
@@ -108,6 +110,7 @@ class SimulationEngine(Executor):
108
110
  )
109
111
  self.verbose = verbose
110
112
 
113
+ # pylint: disable=too-many-locals
111
114
  @override
112
115
  def start_run(
113
116
  self,
@@ -152,6 +155,15 @@ class SimulationEngine(Executor):
152
155
  "Config extracted from FAB's pyproject.toml is not valid"
153
156
  )
154
157
 
158
+ # Flatten federated config
159
+ federation_config_flat = unflatten_dict(federation_config)
160
+
161
+ num_supernodes = federation_config_flat.get(
162
+ "num-supernodes", self.num_supernodes
163
+ )
164
+ backend_cfg = federation_config_flat.get("backend", {})
165
+ verbose: Optional[bool] = federation_config_flat.get("verbose")
166
+
155
167
  # In Simulation there is no SuperLink, still we create a run_id
156
168
  run_id = generate_rand_int_from_bytes(RUN_ID_NUM_BYTES)
157
169
  log(INFO, "Created run %s", str(run_id))
@@ -162,11 +174,18 @@ class SimulationEngine(Executor):
162
174
  "--app",
163
175
  f"{str(fab_path)}",
164
176
  "--num-supernodes",
165
- f"{federation_config.get('num-supernodes', self.num_supernodes)}",
177
+ f"{num_supernodes}",
166
178
  "--run-id",
167
179
  str(run_id),
168
180
  ]
169
181
 
182
+ if backend_cfg:
183
+ # Stringify as JSON
184
+ command.extend(["--backend-config", json.dumps(backend_cfg)])
185
+
186
+ if verbose:
187
+ command.extend(["--verbose"])
188
+
170
189
  if override_config:
171
190
  override_config_str = _user_config_to_str(override_config)
172
191
  command.extend(["--run-config", f"{override_config_str}"])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.11.0.dev20240822
3
+ Version: 1.11.0.dev20240823
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -1,7 +1,7 @@
1
1
  flwr/__init__.py,sha256=VmBWedrCxqmt4QvUHBLqyVEH6p7zaFMD_oCHerXHSVw,937
2
2
  flwr/cli/__init__.py,sha256=cZJVgozlkC6Ni2Hd_FAIrqefrkCGOV18fikToq-6iLw,720
3
3
  flwr/cli/app.py,sha256=FBcSrE35ll88VE11ib67qgsJe2GYDN25UswV9-cYcX8,1267
4
- flwr/cli/build.py,sha256=gIR-nTgmLJY5ZtJFLN5ebFBCN3_hoqaioFT77AHojNU,5159
4
+ flwr/cli/build.py,sha256=YrzjnwP1A1WQnyPuxtGy4kylcPoj0lexaF99ST872VQ,5174
5
5
  flwr/cli/config_utils.py,sha256=mDGXbcIxG14UpkUplILBYUkSk5M1LeTzZYDGNx-pFpU,7540
6
6
  flwr/cli/example.py,sha256=1bGDYll3BXQY2kRqSN-oICqS5n1b9m0g0RvXTopXHl4,2215
7
7
  flwr/cli/install.py,sha256=tUncrbZYRbC9QEcWSeTER16plPEoU-ERP0-nMgWiSPo,7094
@@ -10,7 +10,7 @@ flwr/cli/new/new.py,sha256=VNb31-NLedm-_OK_D0aed0QxHO-tVlXjnf9UWVhC_Jk,9612
10
10
  flwr/cli/new/templates/__init__.py,sha256=4luU8RL-CK8JJCstQ_ON809W9bNTkY1l9zSaPKBkgwY,725
11
11
  flwr/cli/new/templates/app/.gitignore.tpl,sha256=XixnHdyeMB2vwkGtGnwHqoWpH-9WChdyG0GXe57duhc,3078
12
12
  flwr/cli/new/templates/app/README.flowertune.md.tpl,sha256=PqzkGm0g6Zy-vZK9_0EO3f_U6g1r69lGc4UL8kds5Q8,2696
13
- flwr/cli/new/templates/app/README.md.tpl,sha256=9-z-JDEao3QmFWNLQyf-OzHlvWxVSzs2s1MeTKk68Zc,699
13
+ flwr/cli/new/templates/app/README.md.tpl,sha256=t7w4YFZEcJOxAnuJmNPw5-fDdIJu7PfLd8gFJDiBwwo,436
14
14
  flwr/cli/new/templates/app/__init__.py,sha256=DU7QMY7IhMQyuwm_tja66xU0KXTWQFqzfTqwg-_NJdE,729
15
15
  flwr/cli/new/templates/app/code/__init__.py,sha256=EM6vfvgAILKPaPn7H1wMV1Wi01WyZCP_Eg6NxD6oWg8,736
16
16
  flwr/cli/new/templates/app/code/__init__.py.tpl,sha256=J0Gn74E7khpLyKJVNqOPu7ev93vkcu1PZugsbxtABMw,52
@@ -50,11 +50,11 @@ flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=vIO1ArukTC76ogYLNmJ
50
50
  flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=jk_5teoyOVM9QdBea8J-nk10S6TKw81QZiiKB54ATF0,654
51
51
  flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=bRIvPCPvTTI4Eo5b61Rmw8WdDw3sjcohciTXgULN5l8,702
52
52
  flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
53
- flwr/cli/run/run.py,sha256=8SoZsow349rC4KC6bfm4uV7NakqWiZahrqvd1Yb-lt0,7653
53
+ flwr/cli/run/run.py,sha256=8JziIz-NITpCVcGj1OgOdyjxa80SyifV0a_jElXaCU0,7987
54
54
  flwr/cli/utils.py,sha256=l65Ul0YsSBPuypk0uorAtEDmLEYiUrzpCXi6zCg9mJ4,4506
55
55
  flwr/client/__init__.py,sha256=wzJZsYJIHf_8-PMzvfbinyzzjgh1UP1vLrAw2_yEbKI,1345
56
56
  flwr/client/app.py,sha256=o_2bhmlBeZATtWnAPZhL-Q1Ly0QZxc9ou4i7t0HKumE,31956
57
- flwr/client/client.py,sha256=Vp9UkOkoHdNfn6iMYZsj_5m_GICiFfUlKEVaLad-YhM,8183
57
+ flwr/client/client.py,sha256=gy6WVlMUFAp8oevN4xpQPX30vPOIYGVqdbuFlTWkyG4,9080
58
58
  flwr/client/client_app.py,sha256=WcO4r6wrdfaus__3s22D2sYjfcptdgmVujUAYdNE6HU,10393
59
59
  flwr/client/clientapp/__init__.py,sha256=kZqChGnTChQ1WGSUkIlW2S5bc0d0mzDubCAmZUGRpEY,800
60
60
  flwr/client/clientapp/app.py,sha256=4QtblvJsZ0-V-QBbzyNqWV13ugrgJmkZnsHpBCuqgi8,7797
@@ -83,14 +83,14 @@ flwr/client/mod/secure_aggregation/secaggplus_mod.py,sha256=fZTfIELkYS64lpgxQKL6
83
83
  flwr/client/mod/utils.py,sha256=UAJXiB0wwVyLkCkpW_i5BXikdBR65p8sNFr7VNHm2nk,1226
84
84
  flwr/client/node_state.py,sha256=ETr9XmMJ9irFT3shCM4Hv4JWN0ewV-e0S-CFwCh80B8,3953
85
85
  flwr/client/node_state_tests.py,sha256=-4fVsn7y-z9NYBuhq-cjepgxgVuPqqQgDOL4SofrdIo,2239
86
- flwr/client/numpy_client.py,sha256=u76GWAdHmJM88Agm2EgLQSvO8Jnk225mJTk-_TmPjFE,10283
86
+ flwr/client/numpy_client.py,sha256=9rpj5OLmeeDQVzopR1My6A2VS3nkBFw6cmNcMMPYGlQ,11180
87
87
  flwr/client/rest_client/__init__.py,sha256=5KGlp7pjc1dhNRkKlaNtUfQmg8wrRFh9lS3P3uRS-7Q,735
88
- flwr/client/rest_client/connection.py,sha256=SglZC4jpqc_0-VBo9cBHa1_2RO9TfPUULQ49DnYeFS0,12767
88
+ flwr/client/rest_client/connection.py,sha256=21YNE6K6JfyZtwIftx1MGOkM78J9wb4EGGOyLS8ej0E,12767
89
89
  flwr/client/supernode/__init__.py,sha256=SUhWOzcgXRNXk1V9UgB5-FaWukqqrOEajVUHEcPkwyQ,865
90
90
  flwr/client/supernode/app.py,sha256=MOYvLX3VkrslsMQ7V0YdY0GovjXOGsgabYXrvRvHUWc,11856
91
91
  flwr/client/typing.py,sha256=dxoTBnTMfqXr5J7G3y-uNjqxYCddvxhu89spfj4Lm2U,1048
92
92
  flwr/common/__init__.py,sha256=TVaoFEJE158aui1TPZQiJCDZX4RNHRyI8I55VC80HhI,3901
93
- flwr/common/address.py,sha256=wRu1Luezx1PWadwV9OA_KNko01oVvbRnPqfzaDn8QOk,1882
93
+ flwr/common/address.py,sha256=1zvmVIAyYP6JbGkMnXuROzkYJ7aSKbJM754lC_kbh1M,3024
94
94
  flwr/common/config.py,sha256=GN-eKrCuyypLMFgr1kO2d-mwiNNphaFt30UlfbaKJFk,7435
95
95
  flwr/common/constant.py,sha256=1XxuRezsr9fl3xvQNPR2kyFkwNeG_f5vZayv0PFh0kY,3012
96
96
  flwr/common/context.py,sha256=5Bd9RCrhLkYZOVR7vr97OVhzVBHQkS1fUsYiIKTwpxU,2239
@@ -185,7 +185,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
185
185
  flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
186
186
  flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
187
187
  flwr/server/__init__.py,sha256=BxzPhvouvWFGi7CFpI5b4EeVR9XDqbK7Ndqg24EL_Rw,1679
188
- flwr/server/app.py,sha256=YhGK5LKD12yZOXJC6p5deAodweV91c0IInyiHx7PnWA,24252
188
+ flwr/server/app.py,sha256=EsP0yl0khbEVIf0FOZuEDNZ1V3IEK9hwQ7q5TkBpO1w,24458
189
189
  flwr/server/client_manager.py,sha256=T8UDSRJBVD3fyIDI7NTAA-NA7GPrMNNgH2OAF54RRxE,6127
190
190
  flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2399
191
191
  flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
@@ -238,24 +238,24 @@ flwr/server/superlink/ffs/ffs.py,sha256=vzldg6ulYOFwOhLMk9LrUADaJaM6klP9H8_JL76i
238
238
  flwr/server/superlink/ffs/ffs_factory.py,sha256=N_eMuUZggotdGiDQ5r_Tf21xsu_ob0e3jyM6ag7d3kk,1490
239
239
  flwr/server/superlink/fleet/__init__.py,sha256=76od-HhYjOUoZFLFDFCFnNHI4JLAmaXQEAyp7LWlQpc,711
240
240
  flwr/server/superlink/fleet/grpc_adapter/__init__.py,sha256=spBQQJeYz8zPOBOfyMLv87kqWPASGB73AymcLXdFaYA,742
241
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py,sha256=K4LkOsVzIdsR7Py_9cfo6CR-bDocpP15ktHTc2UnJ9k,4957
241
+ flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py,sha256=pBvUEwSFWjKyROE_F2r_j-mX2mxz9snxGwhirpnZB8E,5597
242
242
  flwr/server/superlink/fleet/grpc_bidi/__init__.py,sha256=dkSKQMuMTYh1qSnuN87cAPv_mcdLg3f0PqTABHs8gUE,735
243
243
  flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py,sha256=KCtr4WzoVvYKEgDQFu4XEStXheO7DN2znTiQX6WFA9k,5993
244
244
  flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py,sha256=hh7ykcLMA_ymmD72eWFMfX7D1EUPWhD29n_cI0fS2Nk,6458
245
245
  flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py,sha256=h3EhqgelegVC4EjOXH5birmAnMoCBJcP7jpHYCnHZPk,4887
246
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py,sha256=ROH-dr7TQZ9nVXjkKOzMhxidJguX2li8--nDnXRi-dU,12095
246
+ flwr/server/superlink/fleet/grpc_bidi/grpc_server.py,sha256=DIXCYfPwm5paWq6hccReW0tB7-dTfq4vvMZ7CsiuYf4,12292
247
247
  flwr/server/superlink/fleet/grpc_rere/__init__.py,sha256=j2hyC342am-_Hgp1g80Y3fGDzfTI6n8QOOn2PyWf4eg,758
248
248
  flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=9vZPmdNuRcXsCJQUv9hrzQvdvUJO4-gvxxCHJJTFvGE,4047
249
249
  flwr/server/superlink/fleet/grpc_rere/server_interceptor.py,sha256=DsHj6XaE0pBSWLYFsUYE44NPqx6IWWTJ9sqbh3GpmvY,7961
250
250
  flwr/server/superlink/fleet/message_handler/__init__.py,sha256=h8oLD7uo5lKICPy0rRdKRjTYe62u8PKkT_fA4xF5JPA,731
251
251
  flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=9qDDPwj3txHPo2dNaWQiO3UpGno5Zm9IMhJXnAPZbqg,4439
252
252
  flwr/server/superlink/fleet/rest_rere/__init__.py,sha256=5jbYbAn75sGv-gBwOPDySE0kz96F6dTYLeMrGqNi4lM,735
253
- flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=yoSU-6nCJF9ASHGNpSY69nZbUhPGXkMIKYDgybKQX3c,7672
253
+ flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=3wegOVJadA3pH_2CwZHQ3UweoYFZpqzOTM92GqLGQ9Y,6301
254
254
  flwr/server/superlink/fleet/vce/__init__.py,sha256=36MHKiefnJeyjwMQzVUK4m06Ojon3WDcwZGQsAcyVhQ,783
255
255
  flwr/server/superlink/fleet/vce/backend/__init__.py,sha256=psQjI1DQHOM5uWVXM27l_wPHjfEkMUTP-_8-lEFH1JA,1466
256
- flwr/server/superlink/fleet/vce/backend/backend.py,sha256=KL0eHScWr_YfP2eY3VP8_OOMgZwnRNW7qpu5J-ISpXI,2212
257
- flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=nb_IZNW_ZKoVzM9duqPddUV4C6T1Jpz9VDBVOzpU-WE,6408
258
- flwr/server/superlink/fleet/vce/vce_api.py,sha256=n9SfUaLDgiPVvGVudSC4dZ37vytEDlPn1WA0iUDWJ1I,12750
256
+ flwr/server/superlink/fleet/vce/backend/backend.py,sha256=YugFH_XYclqmq07er5ne1DZc7PgR9lWGs5LY_YIHwc8,2207
257
+ flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=GkeaSe7uXg2iIgKiZNTCwM3TAwYEzuJnr9mA8-vg4hM,6793
258
+ flwr/server/superlink/fleet/vce/vce_api.py,sha256=lTQ2WycKK4-w22EPoJJz1qn73j9ZTwBlNz2cla2Zv0Q,12653
259
259
  flwr/server/superlink/state/__init__.py,sha256=Gj2OTFLXvA-mAjBvwuKDM3rDrVaQPcIoybSa2uskMTE,1003
260
260
  flwr/server/superlink/state/in_memory_state.py,sha256=XMcT5WvKPOrFOuKcByr5BRFacX4aR2n9bRkABUuPg-M,13206
261
261
  flwr/server/superlink/state/sqlite_state.py,sha256=N8eOLZUveJOzdzL31-hXRYOMqV_-w75S1InyDenaDWU,29420
@@ -270,24 +270,24 @@ flwr/server/workflow/__init__.py,sha256=SXY0XkwbkezFBxxrFB5hKUtmtAgnYISBkPouR1V7
270
270
  flwr/server/workflow/constant.py,sha256=q4DLdR8Krlxuewq2AQjwTL75hphxE5ODNz4AhViHMXk,1082
271
271
  flwr/server/workflow/default_workflows.py,sha256=q9f1N2NfOrDrR3S3FH1ePNmTMsUUNwv3cdcAejbGSlk,14086
272
272
  flwr/server/workflow/secure_aggregation/__init__.py,sha256=3XlgDOjD_hcukTGl6Bc1B-8M_dPlVSJuTbvXIbiO-Ic,880
273
- flwr/server/workflow/secure_aggregation/secagg_workflow.py,sha256=wpAkYPId0nfK6SgpUAtsCni4_MQLd-uqJ81tUKu3xlI,5838
274
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=LLW4LfPAVdpMhIjJBopHVl-OltYuVSqsESw3PULcrN8,29694
273
+ flwr/server/workflow/secure_aggregation/secagg_workflow.py,sha256=l2IdMdJjs1bgHs5vQgLSOVzar7v2oxUn46oCrnVE1rM,5839
274
+ flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=vdS_AeRYBvxbSKhM7yaWzfjb21wJfnmr55IAAqIeDGM,29695
275
275
  flwr/simulation/__init__.py,sha256=ybVBW3xT6cg_IasJV4_ymI3bVrCbFhw1mmcfrdfl3q0,1359
276
276
  flwr/simulation/app.py,sha256=te3dQB3eodPwzsv1y4daPyaskIAaOtgoHaQLobrqoqY,15163
277
277
  flwr/simulation/ray_transport/__init__.py,sha256=wzcEEwUUlulnXsg6raCA1nGpP3LlAQDtJ8zNkCXcVbA,734
278
278
  flwr/simulation/ray_transport/ray_actor.py,sha256=3j0HgzjrlYjnzdTRy8aA4Nf6VoUvxi1hGRQkGSU5z6c,19020
279
279
  flwr/simulation/ray_transport/ray_client_proxy.py,sha256=0abIsU0VBk9rNJZOKHIyzYGy3ZnWBgqYocX_oct1EP0,7307
280
280
  flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
281
- flwr/simulation/run_simulation.py,sha256=BXzDF2vMoLTne1NkHKHOGv1FH0fRNy4Ds83Vkr_Qudk,20613
281
+ flwr/simulation/run_simulation.py,sha256=EeioiueoxuBompr04ETUSJp4nNuigamiUEObjVAZHV4,21722
282
282
  flwr/superexec/__init__.py,sha256=9h94ogLxi6eJ3bUuJYq3E3pApThSabTPiSmPAGlTkHE,800
283
283
  flwr/superexec/app.py,sha256=KQuAnyTs2RQMGeIOrJjR3fJ70HsIEF3LQRKazOy27OA,6489
284
284
  flwr/superexec/deployment.py,sha256=1qhztkcZDjaSbicligbXGqn49gbpN271rTlEVAnNuWw,6283
285
285
  flwr/superexec/exec_grpc.py,sha256=PhqGoZEpTMxSQmUSV8Wgtzb1Za_pHJ-adZqo5RYnDyE,1942
286
286
  flwr/superexec/exec_servicer.py,sha256=jl0aKVjm0PLQABcTL5c3jdSIzb0Z6hpVOtrAn4Ob7ts,2323
287
287
  flwr/superexec/executor.py,sha256=k_adivto6R2U82DADOHNvdtobehBYreRek1gOEBIQnQ,2318
288
- flwr/superexec/simulation.py,sha256=lfdClQYSAIMHe43aJ0Pk-kBw_xoV09LsIMfHo2eo-Ck,6775
289
- flwr_nightly-1.11.0.dev20240822.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
290
- flwr_nightly-1.11.0.dev20240822.dist-info/METADATA,sha256=1OtA53Kfj8jYKL3qUYVhcE5TqBzsuwmxR-EalE1b5yw,15701
291
- flwr_nightly-1.11.0.dev20240822.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
292
- flwr_nightly-1.11.0.dev20240822.dist-info/entry_points.txt,sha256=3cDQVJEBRCSLzJrVYAgjXpoCjuQ74I3A9NZ61DOHdVo,388
293
- flwr_nightly-1.11.0.dev20240822.dist-info/RECORD,,
288
+ flwr/superexec/simulation.py,sha256=J6pw-RqCSiUed8I_3MasZH4tl57ZmDebPAHNnbb0-vE,7420
289
+ flwr_nightly-1.11.0.dev20240823.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
290
+ flwr_nightly-1.11.0.dev20240823.dist-info/METADATA,sha256=2Hw-dqtTUCGXfpbCipRr3eOUgknQguj_2hV1CBjG7wA,15701
291
+ flwr_nightly-1.11.0.dev20240823.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
292
+ flwr_nightly-1.11.0.dev20240823.dist-info/entry_points.txt,sha256=3cDQVJEBRCSLzJrVYAgjXpoCjuQ74I3A9NZ61DOHdVo,388
293
+ flwr_nightly-1.11.0.dev20240823.dist-info/RECORD,,