flwr-nightly 1.11.0.dev20240821__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 +1 -1
- flwr/cli/new/templates/app/README.md.tpl +7 -30
- flwr/cli/run/run.py +10 -0
- flwr/client/client.py +22 -1
- flwr/client/numpy_client.py +22 -1
- flwr/client/rest_client/connection.py +1 -1
- flwr/client/supernode/app.py +5 -48
- flwr/common/address.py +43 -0
- flwr/server/app.py +6 -0
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +15 -2
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +5 -0
- flwr/server/superlink/fleet/rest_rere/rest_api.py +71 -122
- flwr/server/superlink/fleet/vce/backend/backend.py +1 -2
- flwr/server/superlink/fleet/vce/backend/raybackend.py +13 -4
- flwr/server/superlink/fleet/vce/vce_api.py +2 -6
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -0
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -0
- flwr/simulation/run_simulation.py +48 -17
- flwr/superexec/simulation.py +20 -1
- {flwr_nightly-1.11.0.dev20240821.dist-info → flwr_nightly-1.11.0.dev20240823.dist-info}/METADATA +1 -1
- {flwr_nightly-1.11.0.dev20240821.dist-info → flwr_nightly-1.11.0.dev20240823.dist-info}/RECORD +24 -24
- {flwr_nightly-1.11.0.dev20240821.dist-info → flwr_nightly-1.11.0.dev20240823.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.11.0.dev20240821.dist-info → flwr_nightly-1.11.0.dev20240823.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.11.0.dev20240821.dist-info → flwr_nightly-1.11.0.dev20240823.dist-info}/entry_points.txt +0 -0
flwr/cli/build.py
CHANGED
|
@@ -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
|
|
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
|
|
17
|
+
## Run with the Deployment Engine
|
|
18
18
|
|
|
19
|
-
|
|
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
|
-
|
|
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
|
flwr/client/numpy_client.py
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
278
|
+
res = _request(req, DeleteNodeResponse, PATH_DELETE_NODE)
|
|
279
279
|
if res is None:
|
|
280
280
|
return
|
|
281
281
|
|
flwr/client/supernode/app.py
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import argparse
|
|
18
18
|
import sys
|
|
19
|
-
from logging import DEBUG, INFO, WARN
|
|
19
|
+
from logging import DEBUG, ERROR, INFO, WARN
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from typing import Optional, Tuple
|
|
22
22
|
|
|
@@ -90,33 +90,12 @@ def run_supernode() -> None:
|
|
|
90
90
|
|
|
91
91
|
def run_client_app() -> None:
|
|
92
92
|
"""Run Flower client app."""
|
|
93
|
-
log(INFO, "Long-running Flower client starting")
|
|
94
|
-
|
|
95
93
|
event(EventType.RUN_CLIENT_APP_ENTER)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
_warn_deprecated_server_arg(args)
|
|
100
|
-
|
|
101
|
-
root_certificates = _get_certificates(args)
|
|
102
|
-
load_fn = get_load_client_app_fn(
|
|
103
|
-
default_app_ref=getattr(args, "client-app"),
|
|
104
|
-
app_path=args.dir,
|
|
105
|
-
multi_app=False,
|
|
106
|
-
)
|
|
107
|
-
authentication_keys = _try_setup_client_authentication(args)
|
|
108
|
-
|
|
109
|
-
start_client_internal(
|
|
110
|
-
server_address=args.superlink,
|
|
111
|
-
node_config=parse_config_args([args.node_config]),
|
|
112
|
-
load_client_app_fn=load_fn,
|
|
113
|
-
transport=args.transport,
|
|
114
|
-
root_certificates=root_certificates,
|
|
115
|
-
insecure=args.insecure,
|
|
116
|
-
authentication_keys=authentication_keys,
|
|
117
|
-
max_retries=args.max_retries,
|
|
118
|
-
max_wait_time=args.max_wait_time,
|
|
94
|
+
log(
|
|
95
|
+
ERROR,
|
|
96
|
+
"The command `flower-client-app` has been replaced by `flower-supernode`.",
|
|
119
97
|
)
|
|
98
|
+
log(INFO, "Execute `flower-supernode --help` to learn how to use it.")
|
|
120
99
|
register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
|
|
121
100
|
|
|
122
101
|
|
|
@@ -227,28 +206,6 @@ def _parse_args_run_supernode() -> argparse.ArgumentParser:
|
|
|
227
206
|
return parser
|
|
228
207
|
|
|
229
208
|
|
|
230
|
-
def _parse_args_run_client_app() -> argparse.ArgumentParser:
|
|
231
|
-
"""Parse flower-client-app command line arguments."""
|
|
232
|
-
parser = argparse.ArgumentParser(
|
|
233
|
-
description="Start a Flower client app",
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
parser.add_argument(
|
|
237
|
-
"client-app",
|
|
238
|
-
help="For example: `client:app` or `project.package.module:wrapper.app`",
|
|
239
|
-
)
|
|
240
|
-
_parse_args_common(parser=parser)
|
|
241
|
-
parser.add_argument(
|
|
242
|
-
"--dir",
|
|
243
|
-
default="",
|
|
244
|
-
help="Add specified directory to the PYTHONPATH and load Flower "
|
|
245
|
-
"app from there."
|
|
246
|
-
" Default: current working directory.",
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
return parser
|
|
250
|
-
|
|
251
|
-
|
|
252
209
|
def _parse_args_common(parser: argparse.ArgumentParser) -> None:
|
|
253
210
|
parser.add_argument(
|
|
254
211
|
"--insecure",
|
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
|
-
|
|
44
|
-
|
|
45
|
-
_check_headers(request.headers)
|
|
55
|
+
GrpcRequest = TypeVar("GrpcRequest", bound=GrpcMessage)
|
|
56
|
+
GrpcResponse = TypeVar("GrpcResponse", bound=GrpcMessage)
|
|
46
57
|
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
)
|
|
67
|
+
def decorator(func: GrpcAsyncFunction[GrpcRequest, GrpcResponse]) -> RestEndPoint:
|
|
68
|
+
async def wrapper(request: Request) -> Response:
|
|
69
|
+
_check_headers(request.headers)
|
|
61
70
|
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
72
|
-
"""Delete Node Id."""
|
|
73
|
-
_check_headers(request.headers)
|
|
83
|
+
return wrapper
|
|
74
84
|
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
)
|
|
146
|
+
return message_handler.get_run(request=request, state=state)
|
|
147
|
+
|
|
201
148
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
(
|
|
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
|
-
|
|
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`
|
|
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
|
|
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
|
|
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,
|
flwr/superexec/simulation.py
CHANGED
|
@@ -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"{
|
|
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}"])
|
{flwr_nightly-1.11.0.dev20240821.dist-info → flwr_nightly-1.11.0.dev20240823.dist-info}/RECORD
RENAMED
|
@@ -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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
88
|
+
flwr/client/rest_client/connection.py,sha256=21YNE6K6JfyZtwIftx1MGOkM78J9wb4EGGOyLS8ej0E,12767
|
|
89
89
|
flwr/client/supernode/__init__.py,sha256=SUhWOzcgXRNXk1V9UgB5-FaWukqqrOEajVUHEcPkwyQ,865
|
|
90
|
-
flwr/client/supernode/app.py,sha256
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
257
|
-
flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=
|
|
258
|
-
flwr/server/superlink/fleet/vce/vce_api.py,sha256=
|
|
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=
|
|
274
|
-
flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=
|
|
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=
|
|
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=
|
|
289
|
-
flwr_nightly-1.11.0.
|
|
290
|
-
flwr_nightly-1.11.0.
|
|
291
|
-
flwr_nightly-1.11.0.
|
|
292
|
-
flwr_nightly-1.11.0.
|
|
293
|
-
flwr_nightly-1.11.0.
|
|
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,,
|
{flwr_nightly-1.11.0.dev20240821.dist-info → flwr_nightly-1.11.0.dev20240823.dist-info}/LICENSE
RENAMED
|
File without changes
|
{flwr_nightly-1.11.0.dev20240821.dist-info → flwr_nightly-1.11.0.dev20240823.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|