flwr-nightly 1.10.0.dev20240715__py3-none-any.whl → 1.10.0.dev20240717__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 +16 -2
- flwr/cli/config_utils.py +23 -15
- flwr/cli/install.py +17 -1
- flwr/cli/new/new.py +4 -3
- flwr/cli/new/templates/app/code/flwr_tune/app.py.tpl +2 -2
- flwr/cli/new/templates/app/code/flwr_tune/server.py.tpl +1 -1
- flwr/cli/new/templates/app/code/server.hf.py.tpl +4 -1
- flwr/cli/new/templates/app/code/server.jax.py.tpl +4 -1
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +4 -1
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +4 -1
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +4 -1
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +4 -1
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +4 -1
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +6 -3
- flwr/cli/new/templates/app/pyproject.hf.toml.tpl +8 -5
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +8 -5
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +8 -5
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +8 -5
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +8 -5
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +8 -5
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +8 -5
- flwr/cli/run/run.py +29 -15
- flwr/client/app.py +3 -3
- flwr/client/node_state.py +17 -3
- flwr/client/supernode/app.py +4 -4
- flwr/common/config.py +28 -17
- flwr/server/run_serverapp.py +1 -1
- flwr/server/superlink/fleet/vce/vce_api.py +10 -3
- flwr/simulation/run_simulation.py +148 -14
- flwr/superexec/app.py +1 -1
- flwr/superexec/deployment.py +1 -9
- flwr/superexec/simulation.py +8 -18
- {flwr_nightly-1.10.0.dev20240715.dist-info → flwr_nightly-1.10.0.dev20240717.dist-info}/METADATA +2 -1
- {flwr_nightly-1.10.0.dev20240715.dist-info → flwr_nightly-1.10.0.dev20240717.dist-info}/RECORD +37 -37
- {flwr_nightly-1.10.0.dev20240715.dist-info → flwr_nightly-1.10.0.dev20240717.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.10.0.dev20240715.dist-info → flwr_nightly-1.10.0.dev20240717.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.10.0.dev20240715.dist-info → flwr_nightly-1.10.0.dev20240717.dist-info}/entry_points.txt +0 -0
flwr/client/app.py
CHANGED
|
@@ -195,7 +195,7 @@ def _start_client_internal(
|
|
|
195
195
|
] = None,
|
|
196
196
|
max_retries: Optional[int] = None,
|
|
197
197
|
max_wait_time: Optional[float] = None,
|
|
198
|
-
|
|
198
|
+
flwr_path: Optional[Path] = None,
|
|
199
199
|
) -> None:
|
|
200
200
|
"""Start a Flower client node which connects to a Flower server.
|
|
201
201
|
|
|
@@ -241,7 +241,7 @@ def _start_client_internal(
|
|
|
241
241
|
The maximum duration before the client stops trying to
|
|
242
242
|
connect to the server in case of connection error.
|
|
243
243
|
If set to None, there is no limit to the total time.
|
|
244
|
-
|
|
244
|
+
flwr_path: Optional[Path] (default: None)
|
|
245
245
|
The fully resolved path containing installed Flower Apps.
|
|
246
246
|
"""
|
|
247
247
|
if insecure is None:
|
|
@@ -402,7 +402,7 @@ def _start_client_internal(
|
|
|
402
402
|
|
|
403
403
|
# Register context for this run
|
|
404
404
|
node_state.register_context(
|
|
405
|
-
run_id=run_id, run=runs[run_id],
|
|
405
|
+
run_id=run_id, run=runs[run_id], flwr_path=flwr_path
|
|
406
406
|
)
|
|
407
407
|
|
|
408
408
|
# Retrieve context for this run
|
flwr/client/node_state.py
CHANGED
|
@@ -20,7 +20,7 @@ from pathlib import Path
|
|
|
20
20
|
from typing import Dict, Optional
|
|
21
21
|
|
|
22
22
|
from flwr.common import Context, RecordSet
|
|
23
|
-
from flwr.common.config import get_fused_config
|
|
23
|
+
from flwr.common.config import get_fused_config, get_fused_config_from_dir
|
|
24
24
|
from flwr.common.typing import Run
|
|
25
25
|
|
|
26
26
|
|
|
@@ -48,11 +48,25 @@ class NodeState:
|
|
|
48
48
|
self,
|
|
49
49
|
run_id: int,
|
|
50
50
|
run: Optional[Run] = None,
|
|
51
|
-
|
|
51
|
+
flwr_path: Optional[Path] = None,
|
|
52
|
+
app_dir: Optional[str] = None,
|
|
52
53
|
) -> None:
|
|
53
54
|
"""Register new run context for this node."""
|
|
54
55
|
if run_id not in self.run_infos:
|
|
55
|
-
initial_run_config =
|
|
56
|
+
initial_run_config = {}
|
|
57
|
+
if app_dir:
|
|
58
|
+
# Load from app directory
|
|
59
|
+
app_path = Path(app_dir)
|
|
60
|
+
if app_path.is_dir():
|
|
61
|
+
override_config = run.override_config if run else {}
|
|
62
|
+
initial_run_config = get_fused_config_from_dir(
|
|
63
|
+
app_path, override_config
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
raise ValueError("The specified `app_dir` must be a directory.")
|
|
67
|
+
else:
|
|
68
|
+
# Load from .fab
|
|
69
|
+
initial_run_config = get_fused_config(run, flwr_path) if run else {}
|
|
56
70
|
self.run_infos[run_id] = RunInfo(
|
|
57
71
|
initial_run_config=initial_run_config,
|
|
58
72
|
context=Context(
|
flwr/client/supernode/app.py
CHANGED
|
@@ -77,8 +77,8 @@ def run_supernode() -> None:
|
|
|
77
77
|
authentication_keys=authentication_keys,
|
|
78
78
|
max_retries=args.max_retries,
|
|
79
79
|
max_wait_time=args.max_wait_time,
|
|
80
|
-
node_config=parse_config_args(args.node_config),
|
|
81
|
-
|
|
80
|
+
node_config=parse_config_args([args.node_config]),
|
|
81
|
+
flwr_path=get_flwr_dir(args.flwr_dir),
|
|
82
82
|
)
|
|
83
83
|
|
|
84
84
|
# Graceful shutdown
|
|
@@ -107,7 +107,7 @@ def run_client_app() -> None:
|
|
|
107
107
|
|
|
108
108
|
_start_client_internal(
|
|
109
109
|
server_address=args.superlink,
|
|
110
|
-
node_config=parse_config_args(args.node_config),
|
|
110
|
+
node_config=parse_config_args([args.node_config]),
|
|
111
111
|
load_client_app_fn=load_fn,
|
|
112
112
|
transport=args.transport,
|
|
113
113
|
root_certificates=root_certificates,
|
|
@@ -248,7 +248,7 @@ def _get_load_client_app_fn(
|
|
|
248
248
|
dir_path = Path(project_dir).absolute()
|
|
249
249
|
|
|
250
250
|
# Set app reference
|
|
251
|
-
client_app_ref = config["tool"]["flwr"]["components"]["clientapp"]
|
|
251
|
+
client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
|
|
252
252
|
|
|
253
253
|
# Set sys.path
|
|
254
254
|
nonlocal inserted_path
|
flwr/common/config.py
CHANGED
|
@@ -86,6 +86,18 @@ def _fuse_dicts(
|
|
|
86
86
|
return fused_dict
|
|
87
87
|
|
|
88
88
|
|
|
89
|
+
def get_fused_config_from_dir(
|
|
90
|
+
project_dir: Path, override_config: Dict[str, str]
|
|
91
|
+
) -> Dict[str, str]:
|
|
92
|
+
"""Merge the overrides from a given dict with the config from a Flower App."""
|
|
93
|
+
default_config = get_project_config(project_dir)["tool"]["flwr"]["app"].get(
|
|
94
|
+
"config", {}
|
|
95
|
+
)
|
|
96
|
+
flat_default_config = flatten_dict(default_config)
|
|
97
|
+
|
|
98
|
+
return _fuse_dicts(flat_default_config, override_config)
|
|
99
|
+
|
|
100
|
+
|
|
89
101
|
def get_fused_config(run: Run, flwr_dir: Optional[Path]) -> Dict[str, str]:
|
|
90
102
|
"""Merge the overrides from a `Run` with the config from a FAB.
|
|
91
103
|
|
|
@@ -97,10 +109,7 @@ def get_fused_config(run: Run, flwr_dir: Optional[Path]) -> Dict[str, str]:
|
|
|
97
109
|
|
|
98
110
|
project_dir = get_project_dir(run.fab_id, run.fab_version, flwr_dir)
|
|
99
111
|
|
|
100
|
-
|
|
101
|
-
flat_default_config = flatten_dict(default_config)
|
|
102
|
-
|
|
103
|
-
return _fuse_dicts(flat_default_config, run.override_config)
|
|
112
|
+
return get_fused_config_from_dir(project_dir, run.override_config)
|
|
104
113
|
|
|
105
114
|
|
|
106
115
|
def flatten_dict(raw_dict: Dict[str, Any], parent_key: str = "") -> Dict[str, str]:
|
|
@@ -121,7 +130,7 @@ def flatten_dict(raw_dict: Dict[str, Any], parent_key: str = "") -> Dict[str, st
|
|
|
121
130
|
|
|
122
131
|
|
|
123
132
|
def parse_config_args(
|
|
124
|
-
config: Optional[str],
|
|
133
|
+
config: Optional[List[str]],
|
|
125
134
|
separator: str = ",",
|
|
126
135
|
) -> Dict[str, str]:
|
|
127
136
|
"""Parse separator separated list of key-value pairs separated by '='."""
|
|
@@ -130,17 +139,19 @@ def parse_config_args(
|
|
|
130
139
|
if config is None:
|
|
131
140
|
return overrides
|
|
132
141
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
for config_line in config:
|
|
143
|
+
if config_line:
|
|
144
|
+
overrides_list = config_line.split(separator)
|
|
145
|
+
if (
|
|
146
|
+
len(overrides_list) == 1
|
|
147
|
+
and "=" not in overrides_list
|
|
148
|
+
and overrides_list[0].endswith(".toml")
|
|
149
|
+
):
|
|
150
|
+
with Path(overrides_list[0]).open("rb") as config_file:
|
|
151
|
+
overrides = flatten_dict(tomli.load(config_file))
|
|
152
|
+
else:
|
|
153
|
+
for kv_pair in overrides_list:
|
|
154
|
+
key, value = kv_pair.split("=")
|
|
155
|
+
overrides[key] = value
|
|
145
156
|
|
|
146
157
|
return overrides
|
flwr/server/run_serverapp.py
CHANGED
|
@@ -186,7 +186,7 @@ def run_server_app() -> None: # pylint: disable=too-many-branches
|
|
|
186
186
|
run_ = driver.run
|
|
187
187
|
server_app_dir = str(get_project_dir(run_.fab_id, run_.fab_version, flwr_dir))
|
|
188
188
|
config = get_project_config(server_app_dir)
|
|
189
|
-
server_app_attr = config["tool"]["flwr"]["components"]["serverapp"]
|
|
189
|
+
server_app_attr = config["tool"]["flwr"]["app"]["components"]["serverapp"]
|
|
190
190
|
server_app_run_config = get_fused_config(run_, flwr_dir)
|
|
191
191
|
else:
|
|
192
192
|
# User provided `server-app`, but not `--run-id`
|
|
@@ -61,7 +61,9 @@ def _register_nodes(
|
|
|
61
61
|
|
|
62
62
|
|
|
63
63
|
def _register_node_states(
|
|
64
|
-
nodes_mapping: NodeToPartitionMapping,
|
|
64
|
+
nodes_mapping: NodeToPartitionMapping,
|
|
65
|
+
run: Run,
|
|
66
|
+
app_dir: Optional[str] = None,
|
|
65
67
|
) -> Dict[int, NodeState]:
|
|
66
68
|
"""Create NodeState objects and pre-register the context for the run."""
|
|
67
69
|
node_states: Dict[int, NodeState] = {}
|
|
@@ -76,7 +78,9 @@ def _register_node_states(
|
|
|
76
78
|
)
|
|
77
79
|
|
|
78
80
|
# Pre-register Context objects
|
|
79
|
-
node_states[node_id].register_context(
|
|
81
|
+
node_states[node_id].register_context(
|
|
82
|
+
run_id=run.run_id, run=run, app_dir=app_dir
|
|
83
|
+
)
|
|
80
84
|
|
|
81
85
|
return node_states
|
|
82
86
|
|
|
@@ -256,6 +260,7 @@ def start_vce(
|
|
|
256
260
|
backend_name: str,
|
|
257
261
|
backend_config_json_stream: str,
|
|
258
262
|
app_dir: str,
|
|
263
|
+
is_app: bool,
|
|
259
264
|
f_stop: threading.Event,
|
|
260
265
|
run: Run,
|
|
261
266
|
flwr_dir: Optional[str] = None,
|
|
@@ -309,7 +314,9 @@ def start_vce(
|
|
|
309
314
|
)
|
|
310
315
|
|
|
311
316
|
# Construct mapping of NodeStates
|
|
312
|
-
node_states = _register_node_states(
|
|
317
|
+
node_states = _register_node_states(
|
|
318
|
+
nodes_mapping=nodes_mapping, run=run, app_dir=app_dir if is_app else None
|
|
319
|
+
)
|
|
313
320
|
|
|
314
321
|
# Load backend config
|
|
315
322
|
log(DEBUG, "Supported backends: %s", list(supported_backends.keys()))
|
|
@@ -18,14 +18,19 @@ import argparse
|
|
|
18
18
|
import asyncio
|
|
19
19
|
import json
|
|
20
20
|
import logging
|
|
21
|
+
import sys
|
|
21
22
|
import threading
|
|
22
23
|
import traceback
|
|
24
|
+
from argparse import Namespace
|
|
23
25
|
from logging import DEBUG, ERROR, INFO, WARNING
|
|
26
|
+
from pathlib import Path
|
|
24
27
|
from time import sleep
|
|
25
|
-
from typing import Dict, Optional
|
|
28
|
+
from typing import Dict, List, Optional
|
|
26
29
|
|
|
30
|
+
from flwr.cli.config_utils import load_and_validate
|
|
27
31
|
from flwr.client import ClientApp
|
|
28
32
|
from flwr.common import EventType, event, log
|
|
33
|
+
from flwr.common.config import get_fused_config_from_dir, parse_config_args
|
|
29
34
|
from flwr.common.constant import RUN_ID_NUM_BYTES
|
|
30
35
|
from flwr.common.logger import set_logger_propagation, update_console_handler
|
|
31
36
|
from flwr.common.typing import Run
|
|
@@ -41,28 +46,129 @@ from flwr.simulation.ray_transport.utils import (
|
|
|
41
46
|
)
|
|
42
47
|
|
|
43
48
|
|
|
49
|
+
def _check_args_do_not_interfere(args: Namespace) -> bool:
|
|
50
|
+
"""Ensure decoupling of flags for different ways to start the simulation."""
|
|
51
|
+
mode_one_args = ["app", "run_config"]
|
|
52
|
+
mode_two_args = ["client_app", "server_app"]
|
|
53
|
+
|
|
54
|
+
def _resolve_message(conflict_keys: List[str]) -> str:
|
|
55
|
+
return ",".join([f"`--{key}`".replace("_", "-") for key in conflict_keys])
|
|
56
|
+
|
|
57
|
+
# When passing `--app`, `--app-dir` is ignored
|
|
58
|
+
if args.app and args.app_dir:
|
|
59
|
+
log(ERROR, "Either `--app` or `--app-dir` can be set, but not both.")
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
if any(getattr(args, key) for key in mode_one_args):
|
|
63
|
+
if any(getattr(args, key) for key in mode_two_args):
|
|
64
|
+
log(
|
|
65
|
+
ERROR,
|
|
66
|
+
"Passing any of {%s} alongside with any of {%s}",
|
|
67
|
+
_resolve_message(mode_one_args),
|
|
68
|
+
_resolve_message(mode_two_args),
|
|
69
|
+
)
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
if not args.app:
|
|
73
|
+
log(ERROR, "You need to pass --app")
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
return True
|
|
77
|
+
|
|
78
|
+
# Ensure all args are set (required for the non-FAB mode of execution)
|
|
79
|
+
if not all(getattr(args, key) for key in mode_two_args):
|
|
80
|
+
log(
|
|
81
|
+
ERROR,
|
|
82
|
+
"Passing all of %s keys are required.",
|
|
83
|
+
_resolve_message(mode_two_args),
|
|
84
|
+
)
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
|
|
44
90
|
# Entry point from CLI
|
|
91
|
+
# pylint: disable=too-many-locals
|
|
45
92
|
def run_simulation_from_cli() -> None:
|
|
46
93
|
"""Run Simulation Engine from the CLI."""
|
|
47
94
|
args = _parse_args_run_simulation().parse_args()
|
|
48
95
|
|
|
96
|
+
# We are supporting two modes for the CLI entrypoint:
|
|
97
|
+
# 1) Running an app dir containing a `pyproject.toml`
|
|
98
|
+
# 2) Running any ClientApp and SeverApp w/o pyproject.toml being present
|
|
99
|
+
# For 2), some CLI args are compulsory, but they are not required for 1)
|
|
100
|
+
# We first do these checks
|
|
101
|
+
args_check_pass = _check_args_do_not_interfere(args)
|
|
102
|
+
if not args_check_pass:
|
|
103
|
+
sys.exit("Simulation Engine cannot start.")
|
|
104
|
+
|
|
105
|
+
run_id = (
|
|
106
|
+
generate_rand_int_from_bytes(RUN_ID_NUM_BYTES)
|
|
107
|
+
if args.run_id is None
|
|
108
|
+
else args.run_id
|
|
109
|
+
)
|
|
110
|
+
if args.app:
|
|
111
|
+
# Mode 1
|
|
112
|
+
app_path = Path(args.app)
|
|
113
|
+
if not app_path.is_dir():
|
|
114
|
+
log(ERROR, "--app is not a directory")
|
|
115
|
+
sys.exit("Simulation Engine cannot start.")
|
|
116
|
+
|
|
117
|
+
# Load pyproject.toml
|
|
118
|
+
config, errors, warnings = load_and_validate(
|
|
119
|
+
app_path / "pyproject.toml", check_module=False
|
|
120
|
+
)
|
|
121
|
+
if errors:
|
|
122
|
+
raise ValueError(errors)
|
|
123
|
+
|
|
124
|
+
if warnings:
|
|
125
|
+
log(WARNING, warnings)
|
|
126
|
+
|
|
127
|
+
if config is None:
|
|
128
|
+
raise ValueError("Config extracted from FAB's pyproject.toml is not valid")
|
|
129
|
+
|
|
130
|
+
# Get ClientApp and SeverApp components
|
|
131
|
+
app_components = config["tool"]["flwr"]["app"]["components"]
|
|
132
|
+
client_app_attr = app_components["clientapp"]
|
|
133
|
+
server_app_attr = app_components["serverapp"]
|
|
134
|
+
|
|
135
|
+
override_config = parse_config_args([args.run_config])
|
|
136
|
+
fused_config = get_fused_config_from_dir(app_path, override_config)
|
|
137
|
+
app_dir = args.app
|
|
138
|
+
is_app = True
|
|
139
|
+
|
|
140
|
+
else:
|
|
141
|
+
# Mode 2
|
|
142
|
+
client_app_attr = args.client_app
|
|
143
|
+
server_app_attr = args.server_app
|
|
144
|
+
override_config = {}
|
|
145
|
+
fused_config = None
|
|
146
|
+
app_dir = args.app_dir
|
|
147
|
+
is_app = False
|
|
148
|
+
|
|
149
|
+
# Create run
|
|
150
|
+
run = Run(
|
|
151
|
+
run_id=run_id,
|
|
152
|
+
fab_id="",
|
|
153
|
+
fab_version="",
|
|
154
|
+
override_config=override_config,
|
|
155
|
+
)
|
|
156
|
+
|
|
49
157
|
# Load JSON config
|
|
50
158
|
backend_config_dict = json.loads(args.backend_config)
|
|
51
159
|
|
|
52
160
|
_run_simulation(
|
|
53
|
-
server_app_attr=
|
|
54
|
-
client_app_attr=
|
|
161
|
+
server_app_attr=server_app_attr,
|
|
162
|
+
client_app_attr=client_app_attr,
|
|
55
163
|
num_supernodes=args.num_supernodes,
|
|
56
164
|
backend_name=args.backend,
|
|
57
165
|
backend_config=backend_config_dict,
|
|
58
|
-
app_dir=
|
|
59
|
-
run=
|
|
60
|
-
Run(run_id=args.run_id, fab_id="", fab_version="", override_config={})
|
|
61
|
-
if args.run_id
|
|
62
|
-
else None
|
|
63
|
-
),
|
|
166
|
+
app_dir=app_dir,
|
|
167
|
+
run=run,
|
|
64
168
|
enable_tf_gpu_growth=args.enable_tf_gpu_growth,
|
|
65
169
|
verbose_logging=args.verbose,
|
|
170
|
+
server_app_run_config=fused_config,
|
|
171
|
+
is_app=is_app,
|
|
66
172
|
)
|
|
67
173
|
|
|
68
174
|
|
|
@@ -205,6 +311,7 @@ def _main_loop(
|
|
|
205
311
|
backend_name: str,
|
|
206
312
|
backend_config_stream: str,
|
|
207
313
|
app_dir: str,
|
|
314
|
+
is_app: bool,
|
|
208
315
|
enable_tf_gpu_growth: bool,
|
|
209
316
|
run: Run,
|
|
210
317
|
flwr_dir: Optional[str] = None,
|
|
@@ -212,6 +319,7 @@ def _main_loop(
|
|
|
212
319
|
client_app_attr: Optional[str] = None,
|
|
213
320
|
server_app: Optional[ServerApp] = None,
|
|
214
321
|
server_app_attr: Optional[str] = None,
|
|
322
|
+
server_app_run_config: Optional[Dict[str, str]] = None,
|
|
215
323
|
) -> None:
|
|
216
324
|
"""Launch SuperLink with Simulation Engine, then ServerApp on a separate thread."""
|
|
217
325
|
# Initialize StateFactory
|
|
@@ -225,7 +333,9 @@ def _main_loop(
|
|
|
225
333
|
# Register run
|
|
226
334
|
log(DEBUG, "Pre-registering run with id %s", run.run_id)
|
|
227
335
|
state_factory.state().run_ids[run.run_id] = run # type: ignore
|
|
228
|
-
|
|
336
|
+
|
|
337
|
+
if server_app_run_config is None:
|
|
338
|
+
server_app_run_config = {}
|
|
229
339
|
|
|
230
340
|
# Initialize Driver
|
|
231
341
|
driver = InMemoryDriver(run_id=run.run_id, state_factory=state_factory)
|
|
@@ -251,6 +361,7 @@ def _main_loop(
|
|
|
251
361
|
backend_name=backend_name,
|
|
252
362
|
backend_config_json_stream=backend_config_stream,
|
|
253
363
|
app_dir=app_dir,
|
|
364
|
+
is_app=is_app,
|
|
254
365
|
state_factory=state_factory,
|
|
255
366
|
f_stop=f_stop,
|
|
256
367
|
run=run,
|
|
@@ -284,11 +395,13 @@ def _run_simulation(
|
|
|
284
395
|
backend_config: Optional[BackendConfig] = None,
|
|
285
396
|
client_app_attr: Optional[str] = None,
|
|
286
397
|
server_app_attr: Optional[str] = None,
|
|
398
|
+
server_app_run_config: Optional[Dict[str, str]] = None,
|
|
287
399
|
app_dir: str = "",
|
|
288
400
|
flwr_dir: Optional[str] = None,
|
|
289
401
|
run: Optional[Run] = None,
|
|
290
402
|
enable_tf_gpu_growth: bool = False,
|
|
291
403
|
verbose_logging: bool = False,
|
|
404
|
+
is_app: bool = False,
|
|
292
405
|
) -> None:
|
|
293
406
|
r"""Launch the Simulation Engine.
|
|
294
407
|
|
|
@@ -317,14 +430,18 @@ def _run_simulation(
|
|
|
317
430
|
parameters. Values supported in <value> are those included by
|
|
318
431
|
`flwr.common.typing.ConfigsRecordValues`.
|
|
319
432
|
|
|
320
|
-
client_app_attr : str
|
|
433
|
+
client_app_attr : Optional[str]
|
|
321
434
|
A path to a `ClientApp` module to be loaded: For example: `client:app` or
|
|
322
435
|
`project.package.module:wrapper.app`."
|
|
323
436
|
|
|
324
|
-
server_app_attr : str
|
|
437
|
+
server_app_attr : Optional[str]
|
|
325
438
|
A path to a `ServerApp` module to be loaded: For example: `server:app` or
|
|
326
439
|
`project.package.module:wrapper.app`."
|
|
327
440
|
|
|
441
|
+
server_app_run_config : Optional[Dict[str, str]]
|
|
442
|
+
Config dictionary that parameterizes the run config. It will be made accesible
|
|
443
|
+
to the ServerApp.
|
|
444
|
+
|
|
328
445
|
app_dir : str
|
|
329
446
|
Add specified directory to the PYTHONPATH and load `ClientApp` from there.
|
|
330
447
|
(Default: current working directory.)
|
|
@@ -346,6 +463,11 @@ def _run_simulation(
|
|
|
346
463
|
verbose_logging : bool (default: False)
|
|
347
464
|
When disabled, only INFO, WARNING and ERROR log messages will be shown. If
|
|
348
465
|
enabled, DEBUG-level logs will be displayed.
|
|
466
|
+
|
|
467
|
+
is_app : bool (default: False)
|
|
468
|
+
A flag that indicates whether the simulation is running an app or not. This is
|
|
469
|
+
needed in order to attempt loading an app's pyproject.toml when nodes register
|
|
470
|
+
a context object.
|
|
349
471
|
"""
|
|
350
472
|
if backend_config is None:
|
|
351
473
|
backend_config = {}
|
|
@@ -381,6 +503,7 @@ def _run_simulation(
|
|
|
381
503
|
backend_name,
|
|
382
504
|
backend_config_stream,
|
|
383
505
|
app_dir,
|
|
506
|
+
is_app,
|
|
384
507
|
enable_tf_gpu_growth,
|
|
385
508
|
run,
|
|
386
509
|
flwr_dir,
|
|
@@ -388,6 +511,7 @@ def _run_simulation(
|
|
|
388
511
|
client_app_attr,
|
|
389
512
|
server_app,
|
|
390
513
|
server_app_attr,
|
|
514
|
+
server_app_run_config,
|
|
391
515
|
)
|
|
392
516
|
# Detect if there is an Asyncio event loop already running.
|
|
393
517
|
# If yes, disable logger propagation. In environmnets
|
|
@@ -419,12 +543,10 @@ def _parse_args_run_simulation() -> argparse.ArgumentParser:
|
|
|
419
543
|
)
|
|
420
544
|
parser.add_argument(
|
|
421
545
|
"--server-app",
|
|
422
|
-
required=True,
|
|
423
546
|
help="For example: `server:app` or `project.package.module:wrapper.app`",
|
|
424
547
|
)
|
|
425
548
|
parser.add_argument(
|
|
426
549
|
"--client-app",
|
|
427
|
-
required=True,
|
|
428
550
|
help="For example: `client:app` or `project.package.module:wrapper.app`",
|
|
429
551
|
)
|
|
430
552
|
parser.add_argument(
|
|
@@ -433,6 +555,18 @@ def _parse_args_run_simulation() -> argparse.ArgumentParser:
|
|
|
433
555
|
required=True,
|
|
434
556
|
help="Number of simulated SuperNodes.",
|
|
435
557
|
)
|
|
558
|
+
parser.add_argument(
|
|
559
|
+
"--app",
|
|
560
|
+
type=str,
|
|
561
|
+
default=None,
|
|
562
|
+
help="Path to a directory containing a FAB-like structure with a "
|
|
563
|
+
"pyproject.toml.",
|
|
564
|
+
)
|
|
565
|
+
parser.add_argument(
|
|
566
|
+
"--run-config",
|
|
567
|
+
default=None,
|
|
568
|
+
help="Override configuration key-value pairs.",
|
|
569
|
+
)
|
|
436
570
|
parser.add_argument(
|
|
437
571
|
"--backend",
|
|
438
572
|
default="ray",
|
flwr/superexec/app.py
CHANGED
|
@@ -56,7 +56,7 @@ def run_superexec() -> None:
|
|
|
56
56
|
address=address,
|
|
57
57
|
executor=_load_executor(args),
|
|
58
58
|
certificates=certificates,
|
|
59
|
-
config=parse_config_args(args.executor_config),
|
|
59
|
+
config=parse_config_args([args.executor_config]),
|
|
60
60
|
)
|
|
61
61
|
|
|
62
62
|
grpc_servers = [superexec_server]
|
flwr/superexec/deployment.py
CHANGED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
"""Deployment engine executor."""
|
|
16
16
|
|
|
17
17
|
import subprocess
|
|
18
|
-
import sys
|
|
19
18
|
from logging import ERROR, INFO
|
|
20
19
|
from pathlib import Path
|
|
21
20
|
from typing import Dict, Optional
|
|
@@ -131,14 +130,7 @@ class DeploymentEngine(Executor):
|
|
|
131
130
|
try:
|
|
132
131
|
# Install FAB to flwr dir
|
|
133
132
|
fab_version, fab_id = get_fab_metadata(fab_file)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
# Install FAB Python package
|
|
137
|
-
subprocess.check_call(
|
|
138
|
-
[sys.executable, "-m", "pip", "install", "--no-deps", str(fab_path)],
|
|
139
|
-
stdout=subprocess.DEVNULL,
|
|
140
|
-
stderr=subprocess.DEVNULL,
|
|
141
|
-
)
|
|
133
|
+
install_from_fab(fab_file, None, True)
|
|
142
134
|
|
|
143
135
|
# Call SuperLink to create run
|
|
144
136
|
run_id: int = self._create_run(fab_id, fab_version, override_config)
|
flwr/superexec/simulation.py
CHANGED
|
@@ -82,11 +82,6 @@ class SimulationEngine(Executor):
|
|
|
82
82
|
) -> Optional[RunTracker]:
|
|
83
83
|
"""Start run using the Flower Simulation Engine."""
|
|
84
84
|
try:
|
|
85
|
-
if override_config:
|
|
86
|
-
raise ValueError(
|
|
87
|
-
"Overriding the run config is not yet supported with the "
|
|
88
|
-
"simulation executor.",
|
|
89
|
-
)
|
|
90
85
|
|
|
91
86
|
# Install FAB to flwr dir
|
|
92
87
|
fab_path = install_from_fab(fab_file, None, True)
|
|
@@ -111,11 +106,6 @@ class SimulationEngine(Executor):
|
|
|
111
106
|
"Config extracted from FAB's pyproject.toml is not valid"
|
|
112
107
|
)
|
|
113
108
|
|
|
114
|
-
# Get ClientApp and SeverApp components
|
|
115
|
-
flower_components = config["tool"]["flwr"]["components"]
|
|
116
|
-
clientapp = flower_components["clientapp"]
|
|
117
|
-
serverapp = flower_components["serverapp"]
|
|
118
|
-
|
|
119
109
|
# In Simulation there is no SuperLink, still we create a run_id
|
|
120
110
|
run_id = generate_rand_int_from_bytes(RUN_ID_NUM_BYTES)
|
|
121
111
|
log(INFO, "Created run %s", str(run_id))
|
|
@@ -123,21 +113,21 @@ class SimulationEngine(Executor):
|
|
|
123
113
|
# Prepare commnand
|
|
124
114
|
command = [
|
|
125
115
|
"flower-simulation",
|
|
126
|
-
"--
|
|
127
|
-
f"{
|
|
128
|
-
"--server-app",
|
|
129
|
-
f"{serverapp}",
|
|
116
|
+
"--app",
|
|
117
|
+
f"{str(fab_path)}",
|
|
130
118
|
"--num-supernodes",
|
|
131
119
|
f"{self.num_supernodes}",
|
|
132
120
|
"--run-id",
|
|
133
121
|
str(run_id),
|
|
134
122
|
]
|
|
135
123
|
|
|
124
|
+
if override_config:
|
|
125
|
+
command.extend(["--run-config", f"{override_config}"])
|
|
126
|
+
|
|
136
127
|
# Start Simulation
|
|
137
|
-
proc = subprocess.
|
|
128
|
+
proc = subprocess.run( # pylint: disable=consider-using-with
|
|
138
129
|
command,
|
|
139
|
-
|
|
140
|
-
stderr=subprocess.PIPE,
|
|
130
|
+
check=True,
|
|
141
131
|
text=True,
|
|
142
132
|
)
|
|
143
133
|
|
|
@@ -145,7 +135,7 @@ class SimulationEngine(Executor):
|
|
|
145
135
|
|
|
146
136
|
return RunTracker(
|
|
147
137
|
run_id=run_id,
|
|
148
|
-
proc=proc,
|
|
138
|
+
proc=proc, # type:ignore
|
|
149
139
|
)
|
|
150
140
|
|
|
151
141
|
# pylint: disable-next=broad-except
|
{flwr_nightly-1.10.0.dev20240715.dist-info → flwr_nightly-1.10.0.dev20240717.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flwr-nightly
|
|
3
|
-
Version: 1.10.0.
|
|
3
|
+
Version: 1.10.0.dev20240717
|
|
4
4
|
Summary: Flower: A Friendly Federated Learning Framework
|
|
5
5
|
Home-page: https://flower.ai
|
|
6
6
|
License: Apache-2.0
|
|
@@ -43,6 +43,7 @@ Requires-Dist: ray (==2.10.0) ; (python_version >= "3.8" and python_version < "3
|
|
|
43
43
|
Requires-Dist: requests (>=2.31.0,<3.0.0) ; extra == "rest"
|
|
44
44
|
Requires-Dist: starlette (>=0.31.0,<0.32.0) ; extra == "rest"
|
|
45
45
|
Requires-Dist: tomli (>=2.0.1,<3.0.0)
|
|
46
|
+
Requires-Dist: tomli-w (>=1.0.0,<2.0.0)
|
|
46
47
|
Requires-Dist: typer[all] (>=0.9.0,<0.10.0)
|
|
47
48
|
Requires-Dist: uvicorn[standard] (>=0.23.0,<0.24.0) ; extra == "rest"
|
|
48
49
|
Project-URL: Documentation, https://flower.ai
|