flwr-nightly 1.10.0.dev20240722__py3-none-any.whl → 1.11.0.dev20240724__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/config_utils.py +13 -15
- flwr/cli/new/new.py +1 -1
- flwr/cli/new/templates/app/code/{client.hf.py.tpl → client.huggingface.py.tpl} +7 -5
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +28 -10
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +7 -5
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +2 -2
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +17 -7
- flwr/cli/new/templates/app/code/flwr_tune/app.py.tpl +20 -17
- flwr/cli/new/templates/app/code/flwr_tune/client.py.tpl +5 -3
- flwr/cli/new/templates/app/code/{server.hf.py.tpl → server.huggingface.py.tpl} +2 -1
- flwr/cli/new/templates/app/code/server.jax.py.tpl +2 -1
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +2 -1
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +2 -1
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +1 -1
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +2 -1
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +1 -1
- flwr/cli/new/templates/app/code/{task.hf.py.tpl → task.huggingface.py.tpl} +13 -1
- flwr/cli/new/templates/app/code/task.mlx.py.tpl +13 -1
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +13 -2
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +13 -1
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +3 -3
- flwr/cli/new/templates/app/{pyproject.hf.toml.tpl → pyproject.huggingface.toml.tpl} +2 -2
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +3 -3
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +8 -8
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +3 -3
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +3 -3
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +6 -6
- flwr/cli/run/run.py +31 -27
- flwr/client/supernode/app.py +12 -43
- flwr/common/config.py +6 -1
- flwr/common/object_ref.py +84 -21
- flwr/proto/exec_pb2.py +16 -12
- flwr/proto/exec_pb2.pyi +20 -1
- flwr/server/run_serverapp.py +0 -3
- flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/vce_api.py +4 -4
- flwr/simulation/__init__.py +1 -1
- flwr/simulation/run_simulation.py +32 -4
- flwr/superexec/app.py +4 -5
- flwr/superexec/deployment.py +1 -2
- flwr/superexec/exec_servicer.py +3 -1
- flwr/superexec/executor.py +3 -0
- flwr/superexec/simulation.py +39 -9
- {flwr_nightly-1.10.0.dev20240722.dist-info → flwr_nightly-1.11.0.dev20240724.dist-info}/METADATA +1 -1
- {flwr_nightly-1.10.0.dev20240722.dist-info → flwr_nightly-1.11.0.dev20240724.dist-info}/RECORD +49 -49
- {flwr_nightly-1.10.0.dev20240722.dist-info → flwr_nightly-1.11.0.dev20240724.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.10.0.dev20240722.dist-info → flwr_nightly-1.11.0.dev20240724.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.10.0.dev20240722.dist-info → flwr_nightly-1.11.0.dev20240724.dist-info}/entry_points.txt +0 -0
|
@@ -23,10 +23,10 @@ serverapp = "$import_name.server_app:app"
|
|
|
23
23
|
clientapp = "$import_name.client_app:app"
|
|
24
24
|
|
|
25
25
|
[tool.flwr.app.config]
|
|
26
|
-
num-server-rounds =
|
|
26
|
+
num-server-rounds = 3
|
|
27
27
|
|
|
28
28
|
[tool.flwr.federations]
|
|
29
|
-
default = "
|
|
29
|
+
default = "local-simulation"
|
|
30
30
|
|
|
31
|
-
[tool.flwr.federations.
|
|
31
|
+
[tool.flwr.federations.local-simulation]
|
|
32
32
|
options.num-supernodes = 10
|
|
@@ -25,11 +25,11 @@ serverapp = "$import_name.server_app:app"
|
|
|
25
25
|
clientapp = "$import_name.client_app:app"
|
|
26
26
|
|
|
27
27
|
[tool.flwr.app.config]
|
|
28
|
-
num-server-rounds =
|
|
29
|
-
local-epochs =
|
|
28
|
+
num-server-rounds = 3
|
|
29
|
+
local-epochs = 1
|
|
30
30
|
|
|
31
31
|
[tool.flwr.federations]
|
|
32
|
-
default = "
|
|
32
|
+
default = "local-simulation"
|
|
33
33
|
|
|
34
|
-
[tool.flwr.federations.
|
|
34
|
+
[tool.flwr.federations.local-simulation]
|
|
35
35
|
options.num-supernodes = 10
|
|
@@ -24,10 +24,10 @@ serverapp = "$import_name.server_app:app"
|
|
|
24
24
|
clientapp = "$import_name.client_app:app"
|
|
25
25
|
|
|
26
26
|
[tool.flwr.app.config]
|
|
27
|
-
num-server-rounds =
|
|
27
|
+
num-server-rounds = 3
|
|
28
28
|
|
|
29
29
|
[tool.flwr.federations]
|
|
30
|
-
default = "
|
|
30
|
+
default = "local-simulation"
|
|
31
31
|
|
|
32
|
-
[tool.flwr.federations.
|
|
32
|
+
[tool.flwr.federations.local-simulation]
|
|
33
33
|
options.num-supernodes = 10
|
|
@@ -24,13 +24,13 @@ serverapp = "$import_name.server_app:app"
|
|
|
24
24
|
clientapp = "$import_name.client_app:app"
|
|
25
25
|
|
|
26
26
|
[tool.flwr.app.config]
|
|
27
|
-
num-server-rounds =
|
|
28
|
-
local-epochs =
|
|
29
|
-
batch-size =
|
|
30
|
-
verbose =
|
|
27
|
+
num-server-rounds = 3
|
|
28
|
+
local-epochs = 1
|
|
29
|
+
batch-size = 32
|
|
30
|
+
verbose = false
|
|
31
31
|
|
|
32
32
|
[tool.flwr.federations]
|
|
33
|
-
default = "
|
|
33
|
+
default = "local-simulation"
|
|
34
34
|
|
|
35
|
-
[tool.flwr.federations.
|
|
35
|
+
[tool.flwr.federations.local-simulation]
|
|
36
36
|
options.num-supernodes = 10
|
flwr/cli/run/run.py
CHANGED
|
@@ -25,7 +25,7 @@ from typing_extensions import Annotated
|
|
|
25
25
|
|
|
26
26
|
from flwr.cli.build import build
|
|
27
27
|
from flwr.cli.config_utils import load_and_validate
|
|
28
|
-
from flwr.common.config import parse_config_args
|
|
28
|
+
from flwr.common.config import flatten_dict, parse_config_args
|
|
29
29
|
from flwr.common.grpc import GRPC_MAX_MESSAGE_LENGTH, create_channel
|
|
30
30
|
from flwr.common.logger import log
|
|
31
31
|
from flwr.common.serde import user_config_to_proto
|
|
@@ -35,27 +35,30 @@ from flwr.proto.exec_pb2_grpc import ExecStub
|
|
|
35
35
|
|
|
36
36
|
# pylint: disable-next=too-many-locals
|
|
37
37
|
def run(
|
|
38
|
-
|
|
38
|
+
app_dir: Annotated[
|
|
39
39
|
Path,
|
|
40
|
-
typer.Argument(help="Path of the Flower project to run"),
|
|
40
|
+
typer.Argument(help="Path of the Flower project to run."),
|
|
41
41
|
] = Path("."),
|
|
42
|
-
|
|
42
|
+
federation: Annotated[
|
|
43
43
|
Optional[str],
|
|
44
|
-
typer.Argument(help="Name of the federation to run the app on"),
|
|
44
|
+
typer.Argument(help="Name of the federation to run the app on."),
|
|
45
45
|
] = None,
|
|
46
46
|
config_overrides: Annotated[
|
|
47
47
|
Optional[List[str]],
|
|
48
48
|
typer.Option(
|
|
49
49
|
"--run-config",
|
|
50
50
|
"-c",
|
|
51
|
-
help="Override configuration key-value pairs"
|
|
51
|
+
help="Override configuration key-value pairs, should be of the format:\n\n"
|
|
52
|
+
"`--run-config key1=value1,key2=value2 --run-config key3=value3`\n\n"
|
|
53
|
+
"Note that `key1`, `key2`, and `key3` in this example need to exist "
|
|
54
|
+
"inside the `pyproject.toml` in order to be properly overriden.",
|
|
52
55
|
),
|
|
53
56
|
] = None,
|
|
54
57
|
) -> None:
|
|
55
58
|
"""Run Flower project."""
|
|
56
59
|
typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
|
|
57
60
|
|
|
58
|
-
pyproject_path =
|
|
61
|
+
pyproject_path = app_dir / "pyproject.toml" if app_dir else None
|
|
59
62
|
config, errors, warnings = load_and_validate(path=pyproject_path)
|
|
60
63
|
|
|
61
64
|
if config is None:
|
|
@@ -78,11 +81,9 @@ def run(
|
|
|
78
81
|
|
|
79
82
|
typer.secho("Success", fg=typer.colors.GREEN)
|
|
80
83
|
|
|
81
|
-
|
|
82
|
-
"default"
|
|
83
|
-
)
|
|
84
|
+
federation = federation or config["tool"]["flwr"]["federations"].get("default")
|
|
84
85
|
|
|
85
|
-
if
|
|
86
|
+
if federation is None:
|
|
86
87
|
typer.secho(
|
|
87
88
|
"❌ No federation name was provided and the project's `pyproject.toml` "
|
|
88
89
|
"doesn't declare a default federation (with a SuperExec address or an "
|
|
@@ -93,13 +94,13 @@ def run(
|
|
|
93
94
|
raise typer.Exit(code=1)
|
|
94
95
|
|
|
95
96
|
# Validate the federation exists in the configuration
|
|
96
|
-
|
|
97
|
-
if
|
|
97
|
+
federation_config = config["tool"]["flwr"]["federations"].get(federation)
|
|
98
|
+
if federation_config is None:
|
|
98
99
|
available_feds = {
|
|
99
100
|
fed for fed in config["tool"]["flwr"]["federations"] if fed != "default"
|
|
100
101
|
}
|
|
101
102
|
typer.secho(
|
|
102
|
-
f"❌ There is no `{
|
|
103
|
+
f"❌ There is no `{federation}` federation declared in "
|
|
103
104
|
"`pyproject.toml`.\n The following federations were found:\n\n"
|
|
104
105
|
+ "\n".join(available_feds),
|
|
105
106
|
fg=typer.colors.RED,
|
|
@@ -107,15 +108,15 @@ def run(
|
|
|
107
108
|
)
|
|
108
109
|
raise typer.Exit(code=1)
|
|
109
110
|
|
|
110
|
-
if "address" in
|
|
111
|
-
_run_with_superexec(
|
|
111
|
+
if "address" in federation_config:
|
|
112
|
+
_run_with_superexec(federation_config, app_dir, config_overrides)
|
|
112
113
|
else:
|
|
113
|
-
_run_without_superexec(
|
|
114
|
+
_run_without_superexec(app_dir, federation_config, federation, config_overrides)
|
|
114
115
|
|
|
115
116
|
|
|
116
117
|
def _run_with_superexec(
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
federation_config: Dict[str, Any],
|
|
119
|
+
app_dir: Optional[Path],
|
|
119
120
|
config_overrides: Optional[List[str]],
|
|
120
121
|
) -> None:
|
|
121
122
|
|
|
@@ -123,8 +124,8 @@ def _run_with_superexec(
|
|
|
123
124
|
"""Log channel connectivity."""
|
|
124
125
|
log(DEBUG, channel_connectivity)
|
|
125
126
|
|
|
126
|
-
insecure_str =
|
|
127
|
-
if root_certificates :=
|
|
127
|
+
insecure_str = federation_config.get("insecure")
|
|
128
|
+
if root_certificates := federation_config.get("root-certificates"):
|
|
128
129
|
root_certificates_bytes = Path(root_certificates).read_bytes()
|
|
129
130
|
if insecure := bool(insecure_str):
|
|
130
131
|
typer.secho(
|
|
@@ -152,7 +153,7 @@ def _run_with_superexec(
|
|
|
152
153
|
raise typer.Exit(code=1)
|
|
153
154
|
|
|
154
155
|
channel = create_channel(
|
|
155
|
-
server_address=
|
|
156
|
+
server_address=federation_config["address"],
|
|
156
157
|
insecure=insecure,
|
|
157
158
|
root_certificates=root_certificates_bytes,
|
|
158
159
|
max_message_length=GRPC_MAX_MESSAGE_LENGTH,
|
|
@@ -161,13 +162,16 @@ def _run_with_superexec(
|
|
|
161
162
|
channel.subscribe(on_channel_state_change)
|
|
162
163
|
stub = ExecStub(channel)
|
|
163
164
|
|
|
164
|
-
fab_path = build(
|
|
165
|
+
fab_path = build(app_dir)
|
|
165
166
|
|
|
166
167
|
req = StartRunRequest(
|
|
167
168
|
fab_file=Path(fab_path).read_bytes(),
|
|
168
169
|
override_config=user_config_to_proto(
|
|
169
170
|
parse_config_args(config_overrides, separator=",")
|
|
170
171
|
),
|
|
172
|
+
federation_config=user_config_to_proto(
|
|
173
|
+
flatten_dict(federation_config.get("options"))
|
|
174
|
+
),
|
|
171
175
|
)
|
|
172
176
|
res = stub.StartRun(req)
|
|
173
177
|
typer.secho(f"🎊 Successfully started run {res.run_id}", fg=typer.colors.GREEN)
|
|
@@ -175,18 +179,18 @@ def _run_with_superexec(
|
|
|
175
179
|
|
|
176
180
|
def _run_without_superexec(
|
|
177
181
|
app_path: Optional[Path],
|
|
178
|
-
|
|
179
|
-
|
|
182
|
+
federation_config: Dict[str, Any],
|
|
183
|
+
federation: str,
|
|
180
184
|
config_overrides: Optional[List[str]],
|
|
181
185
|
) -> None:
|
|
182
186
|
try:
|
|
183
|
-
num_supernodes =
|
|
187
|
+
num_supernodes = federation_config["options"]["num-supernodes"]
|
|
184
188
|
except KeyError as err:
|
|
185
189
|
typer.secho(
|
|
186
190
|
"❌ The project's `pyproject.toml` needs to declare the number of"
|
|
187
191
|
" SuperNodes in the simulation. To simulate 10 SuperNodes,"
|
|
188
192
|
" use the following notation:\n\n"
|
|
189
|
-
f"[tool.flwr.federations.{
|
|
193
|
+
f"[tool.flwr.federations.{federation}]\n"
|
|
190
194
|
"options.num-supernodes = 10\n",
|
|
191
195
|
fg=typer.colors.RED,
|
|
192
196
|
bold=True,
|
flwr/client/supernode/app.py
CHANGED
|
@@ -62,8 +62,8 @@ def run_supernode() -> None:
|
|
|
62
62
|
root_certificates = _get_certificates(args)
|
|
63
63
|
load_fn = _get_load_client_app_fn(
|
|
64
64
|
default_app_ref=getattr(args, "client-app"),
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
project_dir=args.dir,
|
|
66
|
+
flwr_dir=args.flwr_dir,
|
|
67
67
|
multi_app=True,
|
|
68
68
|
)
|
|
69
69
|
authentication_keys = _try_setup_client_authentication(args)
|
|
@@ -100,7 +100,7 @@ def run_client_app() -> None:
|
|
|
100
100
|
root_certificates = _get_certificates(args)
|
|
101
101
|
load_fn = _get_load_client_app_fn(
|
|
102
102
|
default_app_ref=getattr(args, "client-app"),
|
|
103
|
-
|
|
103
|
+
project_dir=args.dir,
|
|
104
104
|
multi_app=False,
|
|
105
105
|
)
|
|
106
106
|
authentication_keys = _try_setup_client_authentication(args)
|
|
@@ -176,9 +176,9 @@ def _get_certificates(args: argparse.Namespace) -> Optional[bytes]:
|
|
|
176
176
|
|
|
177
177
|
def _get_load_client_app_fn(
|
|
178
178
|
default_app_ref: str,
|
|
179
|
-
|
|
179
|
+
project_dir: str,
|
|
180
180
|
multi_app: bool,
|
|
181
|
-
|
|
181
|
+
flwr_dir: Optional[str] = None,
|
|
182
182
|
) -> Callable[[str, str], ClientApp]:
|
|
183
183
|
"""Get the load_client_app_fn function.
|
|
184
184
|
|
|
@@ -189,38 +189,21 @@ def _get_load_client_app_fn(
|
|
|
189
189
|
If `multi_app` is False, it ignores `fab_id` and `fab_version` and
|
|
190
190
|
loads a default ClientApp.
|
|
191
191
|
"""
|
|
192
|
-
# Find the Flower directory containing Flower Apps (only for multi-app)
|
|
193
|
-
if not multi_app:
|
|
194
|
-
flwr_dir = Path("")
|
|
195
|
-
else:
|
|
196
|
-
if flwr_dir_arg is None:
|
|
197
|
-
flwr_dir = get_flwr_dir()
|
|
198
|
-
else:
|
|
199
|
-
flwr_dir = Path(flwr_dir_arg).absolute()
|
|
200
|
-
|
|
201
|
-
inserted_path = None
|
|
202
|
-
|
|
203
192
|
if not multi_app:
|
|
204
193
|
log(
|
|
205
194
|
DEBUG,
|
|
206
195
|
"Flower SuperNode will load and validate ClientApp `%s`",
|
|
207
196
|
default_app_ref,
|
|
208
197
|
)
|
|
209
|
-
# Insert sys.path
|
|
210
|
-
dir_path = Path(dir_arg).absolute()
|
|
211
|
-
sys.path.insert(0, str(dir_path))
|
|
212
|
-
inserted_path = str(dir_path)
|
|
213
198
|
|
|
214
|
-
valid, error_msg = validate(default_app_ref)
|
|
199
|
+
valid, error_msg = validate(default_app_ref, project_dir=project_dir)
|
|
215
200
|
if not valid and error_msg:
|
|
216
201
|
raise LoadClientAppError(error_msg) from None
|
|
217
202
|
|
|
218
203
|
def _load(fab_id: str, fab_version: str) -> ClientApp:
|
|
204
|
+
runtime_project_dir = Path(project_dir).absolute()
|
|
219
205
|
# If multi-app feature is disabled
|
|
220
206
|
if not multi_app:
|
|
221
|
-
# Get sys path to be inserted
|
|
222
|
-
dir_path = Path(dir_arg).absolute()
|
|
223
|
-
|
|
224
207
|
# Set app reference
|
|
225
208
|
client_app_ref = default_app_ref
|
|
226
209
|
# If multi-app feature is enabled but the fab id is not specified
|
|
@@ -231,43 +214,29 @@ def _get_load_client_app_fn(
|
|
|
231
214
|
) from None
|
|
232
215
|
|
|
233
216
|
log(WARN, "FAB ID is not provided; the default ClientApp will be loaded.")
|
|
234
|
-
# Get sys path to be inserted
|
|
235
|
-
dir_path = Path(dir_arg).absolute()
|
|
236
217
|
|
|
237
218
|
# Set app reference
|
|
238
219
|
client_app_ref = default_app_ref
|
|
239
220
|
# If multi-app feature is enabled
|
|
240
221
|
else:
|
|
241
222
|
try:
|
|
242
|
-
|
|
243
|
-
|
|
223
|
+
runtime_project_dir = get_project_dir(
|
|
224
|
+
fab_id, fab_version, get_flwr_dir(flwr_dir)
|
|
225
|
+
)
|
|
226
|
+
config = get_project_config(runtime_project_dir)
|
|
244
227
|
except Exception as e:
|
|
245
228
|
raise LoadClientAppError("Failed to load ClientApp") from e
|
|
246
229
|
|
|
247
|
-
# Get sys path to be inserted
|
|
248
|
-
dir_path = Path(project_dir).absolute()
|
|
249
|
-
|
|
250
230
|
# Set app reference
|
|
251
231
|
client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
|
|
252
232
|
|
|
253
|
-
# Set sys.path
|
|
254
|
-
nonlocal inserted_path
|
|
255
|
-
if inserted_path != str(dir_path):
|
|
256
|
-
# Remove the previously inserted path
|
|
257
|
-
if inserted_path is not None:
|
|
258
|
-
sys.path.remove(inserted_path)
|
|
259
|
-
# Insert the new path
|
|
260
|
-
sys.path.insert(0, str(dir_path))
|
|
261
|
-
|
|
262
|
-
inserted_path = str(dir_path)
|
|
263
|
-
|
|
264
233
|
# Load ClientApp
|
|
265
234
|
log(
|
|
266
235
|
DEBUG,
|
|
267
236
|
"Loading ClientApp `%s`",
|
|
268
237
|
client_app_ref,
|
|
269
238
|
)
|
|
270
|
-
client_app = load_app(client_app_ref, LoadClientAppError,
|
|
239
|
+
client_app = load_app(client_app_ref, LoadClientAppError, runtime_project_dir)
|
|
271
240
|
|
|
272
241
|
if not isinstance(client_app, ClientApp):
|
|
273
242
|
raise LoadClientAppError(
|
flwr/common/config.py
CHANGED
|
@@ -113,8 +113,13 @@ def get_fused_config(run: Run, flwr_dir: Optional[Path]) -> UserConfig:
|
|
|
113
113
|
return get_fused_config_from_dir(project_dir, run.override_config)
|
|
114
114
|
|
|
115
115
|
|
|
116
|
-
def flatten_dict(
|
|
116
|
+
def flatten_dict(
|
|
117
|
+
raw_dict: Optional[Dict[str, Any]], parent_key: str = ""
|
|
118
|
+
) -> UserConfig:
|
|
117
119
|
"""Flatten dict by joining nested keys with a given separator."""
|
|
120
|
+
if raw_dict is None:
|
|
121
|
+
return {}
|
|
122
|
+
|
|
118
123
|
items: List[Tuple[str, UserConfigValue]] = []
|
|
119
124
|
separator: str = "."
|
|
120
125
|
for k, v in raw_dict.items():
|
flwr/common/object_ref.py
CHANGED
|
@@ -33,22 +33,41 @@ attribute.
|
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
_current_sys_path: Optional[str] = None
|
|
37
|
+
|
|
38
|
+
|
|
36
39
|
def validate(
|
|
37
40
|
module_attribute_str: str,
|
|
38
41
|
check_module: bool = True,
|
|
42
|
+
project_dir: Optional[Union[str, Path]] = None,
|
|
39
43
|
) -> Tuple[bool, Optional[str]]:
|
|
40
44
|
"""Validate object reference.
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
module_attribute_str : str
|
|
49
|
+
The reference to the object. It should have the form `<module>:<attribute>`.
|
|
50
|
+
Valid examples include `client:app` and `project.package.module:wrapper.app`.
|
|
51
|
+
It must refer to a module on the PYTHONPATH or in the provided `project_dir`
|
|
52
|
+
and the module needs to have the specified attribute.
|
|
53
|
+
check_module : bool (default: True)
|
|
54
|
+
Flag indicating whether to verify the existence of the module and the
|
|
55
|
+
specified attribute within it.
|
|
56
|
+
project_dir : Optional[Union[str, Path]] (default: None)
|
|
57
|
+
The directory containing the module. If None, the current working directory
|
|
58
|
+
is used. If `check_module` is True, the `project_dir` will be inserted into
|
|
59
|
+
the system path, and the previously inserted `project_dir` will be removed.
|
|
46
60
|
|
|
47
61
|
Returns
|
|
48
62
|
-------
|
|
49
63
|
Tuple[bool, Optional[str]]
|
|
50
64
|
A boolean indicating whether an object reference is valid and
|
|
51
65
|
the reason why it might not be.
|
|
66
|
+
|
|
67
|
+
Note
|
|
68
|
+
----
|
|
69
|
+
This function will modify `sys.path` by inserting the provided `project_dir`
|
|
70
|
+
and removing the previously inserted `project_dir`.
|
|
52
71
|
"""
|
|
53
72
|
module_str, _, attributes_str = module_attribute_str.partition(":")
|
|
54
73
|
if not module_str:
|
|
@@ -63,6 +82,9 @@ def validate(
|
|
|
63
82
|
)
|
|
64
83
|
|
|
65
84
|
if check_module:
|
|
85
|
+
# Set the system path
|
|
86
|
+
_set_sys_path(project_dir)
|
|
87
|
+
|
|
66
88
|
# Load module
|
|
67
89
|
module = find_spec(module_str)
|
|
68
90
|
if module and module.origin:
|
|
@@ -89,18 +111,40 @@ def load_app( # pylint: disable= too-many-branches
|
|
|
89
111
|
) -> Any:
|
|
90
112
|
"""Return the object specified in a module attribute string.
|
|
91
113
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
module_attribute_str : str
|
|
117
|
+
The reference to the object. It should have the form `<module>:<attribute>`.
|
|
118
|
+
Valid examples include `client:app` and `project.package.module:wrapper.app`.
|
|
119
|
+
It must refer to a module on the PYTHONPATH or in the provided `project_dir`
|
|
120
|
+
and the module needs to have the specified attribute.
|
|
121
|
+
error_type : Type[Exception]
|
|
122
|
+
The type of exception to be raised if the provided `module_attribute_str` is
|
|
123
|
+
in an invalid format.
|
|
124
|
+
project_dir : Optional[Union[str, Path]], optional (default=None)
|
|
125
|
+
The directory containing the module. If None, the current working directory
|
|
126
|
+
is used. The `project_dir` will be inserted into the system path, and the
|
|
127
|
+
previously inserted `project_dir` will be removed.
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
Any
|
|
132
|
+
The object specified by the module attribute string.
|
|
133
|
+
|
|
134
|
+
Note
|
|
135
|
+
----
|
|
136
|
+
This function will modify `sys.path` by inserting the provided `project_dir`
|
|
137
|
+
and removing the previously inserted `project_dir`.
|
|
96
138
|
"""
|
|
97
|
-
valid, error_msg = validate(module_attribute_str)
|
|
139
|
+
valid, error_msg = validate(module_attribute_str, check_module=False)
|
|
98
140
|
if not valid and error_msg:
|
|
99
141
|
raise error_type(error_msg) from None
|
|
100
142
|
|
|
101
143
|
module_str, _, attributes_str = module_attribute_str.partition(":")
|
|
102
144
|
|
|
103
145
|
try:
|
|
146
|
+
_set_sys_path(project_dir)
|
|
147
|
+
|
|
104
148
|
if module_str not in sys.modules:
|
|
105
149
|
module = importlib.import_module(module_str)
|
|
106
150
|
# Hack: `tabnet` does not work with `importlib.reload`
|
|
@@ -116,19 +160,15 @@ def load_app( # pylint: disable= too-many-branches
|
|
|
116
160
|
module = sys.modules[module_str]
|
|
117
161
|
else:
|
|
118
162
|
module = sys.modules[module_str]
|
|
163
|
+
|
|
119
164
|
if project_dir is None:
|
|
120
|
-
|
|
121
|
-
if path is not None:
|
|
122
|
-
project_dir = str(Path(path).parent)
|
|
123
|
-
else:
|
|
124
|
-
project_dir = str(Path(project_dir).absolute())
|
|
165
|
+
project_dir = Path.cwd()
|
|
125
166
|
|
|
126
167
|
# Reload cached modules in the project directory
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
importlib.reload(m)
|
|
168
|
+
for m in list(sys.modules.values()):
|
|
169
|
+
path: Optional[str] = getattr(m, "__file__", None)
|
|
170
|
+
if path is not None and path.startswith(str(project_dir)):
|
|
171
|
+
importlib.reload(m)
|
|
132
172
|
|
|
133
173
|
except ModuleNotFoundError as err:
|
|
134
174
|
raise error_type(
|
|
@@ -140,15 +180,38 @@ def load_app( # pylint: disable= too-many-branches
|
|
|
140
180
|
try:
|
|
141
181
|
for attribute_str in attributes_str.split("."):
|
|
142
182
|
attribute = getattr(attribute, attribute_str)
|
|
143
|
-
except AttributeError:
|
|
183
|
+
except AttributeError as err:
|
|
144
184
|
raise error_type(
|
|
145
185
|
f"Unable to load attribute {attributes_str} from module {module_str}"
|
|
146
186
|
f"{OBJECT_REF_HELP_STR}",
|
|
147
|
-
) from
|
|
187
|
+
) from err
|
|
148
188
|
|
|
149
189
|
return attribute
|
|
150
190
|
|
|
151
191
|
|
|
192
|
+
def _set_sys_path(directory: Optional[Union[str, Path]]) -> None:
|
|
193
|
+
"""Set the system path."""
|
|
194
|
+
if directory is None:
|
|
195
|
+
directory = Path.cwd()
|
|
196
|
+
else:
|
|
197
|
+
directory = Path(directory).absolute()
|
|
198
|
+
|
|
199
|
+
# If the directory has already been added to `sys.path`, return
|
|
200
|
+
if str(directory) in sys.path:
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
# Remove the old path if it exists and is not `""`.
|
|
204
|
+
global _current_sys_path # pylint: disable=global-statement
|
|
205
|
+
if _current_sys_path is not None:
|
|
206
|
+
sys.path.remove(_current_sys_path)
|
|
207
|
+
|
|
208
|
+
# Add the new path to sys.path
|
|
209
|
+
sys.path.insert(0, str(directory))
|
|
210
|
+
|
|
211
|
+
# Update the current_sys_path
|
|
212
|
+
_current_sys_path = str(directory)
|
|
213
|
+
|
|
214
|
+
|
|
152
215
|
def _find_attribute_in_module(file_path: str, attribute_name: str) -> bool:
|
|
153
216
|
"""Check if attribute_name exists in module's abstract symbolic tree."""
|
|
154
217
|
with open(file_path, encoding="utf-8") as file:
|
flwr/proto/exec_pb2.py
CHANGED
|
@@ -15,7 +15,7 @@ _sym_db = _symbol_database.Default()
|
|
|
15
15
|
from flwr.proto import transport_pb2 as flwr_dot_proto_dot_transport__pb2
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\x1a\x1a\x66lwr/proto/transport.proto\"\
|
|
18
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\x1a\x1a\x66lwr/proto/transport.proto\"\xd3\x02\n\x0fStartRunRequest\x12\x10\n\x08\x66\x61\x62_file\x18\x01 \x01(\x0c\x12H\n\x0foverride_config\x18\x02 \x03(\x0b\x32/.flwr.proto.StartRunRequest.OverrideConfigEntry\x12L\n\x11\x66\x65\x64\x65ration_config\x18\x03 \x03(\x0b\x32\x31.flwr.proto.StartRunRequest.FederationConfigEntry\x1aI\n\x13OverrideConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\x1aK\n\x15\x46\x65\x64\x65rationConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"\"\n\x10StartRunResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\x12\"#\n\x11StreamLogsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x12\"(\n\x12StreamLogsResponse\x12\x12\n\nlog_output\x18\x01 \x01(\t2\xa0\x01\n\x04\x45xec\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x12O\n\nStreamLogs\x12\x1d.flwr.proto.StreamLogsRequest\x1a\x1e.flwr.proto.StreamLogsResponse\"\x00\x30\x01\x62\x06proto3')
|
|
19
19
|
|
|
20
20
|
_globals = globals()
|
|
21
21
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -24,16 +24,20 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
|
|
24
24
|
DESCRIPTOR._options = None
|
|
25
25
|
_globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._options = None
|
|
26
26
|
_globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._serialized_options = b'8\001'
|
|
27
|
+
_globals['_STARTRUNREQUEST_FEDERATIONCONFIGENTRY']._options = None
|
|
28
|
+
_globals['_STARTRUNREQUEST_FEDERATIONCONFIGENTRY']._serialized_options = b'8\001'
|
|
27
29
|
_globals['_STARTRUNREQUEST']._serialized_start=66
|
|
28
|
-
_globals['_STARTRUNREQUEST']._serialized_end=
|
|
29
|
-
_globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._serialized_start=
|
|
30
|
-
_globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._serialized_end=
|
|
31
|
-
_globals['
|
|
32
|
-
_globals['
|
|
33
|
-
_globals['
|
|
34
|
-
_globals['
|
|
35
|
-
_globals['
|
|
36
|
-
_globals['
|
|
37
|
-
_globals['
|
|
38
|
-
_globals['
|
|
30
|
+
_globals['_STARTRUNREQUEST']._serialized_end=405
|
|
31
|
+
_globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._serialized_start=255
|
|
32
|
+
_globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._serialized_end=328
|
|
33
|
+
_globals['_STARTRUNREQUEST_FEDERATIONCONFIGENTRY']._serialized_start=330
|
|
34
|
+
_globals['_STARTRUNREQUEST_FEDERATIONCONFIGENTRY']._serialized_end=405
|
|
35
|
+
_globals['_STARTRUNRESPONSE']._serialized_start=407
|
|
36
|
+
_globals['_STARTRUNRESPONSE']._serialized_end=441
|
|
37
|
+
_globals['_STREAMLOGSREQUEST']._serialized_start=443
|
|
38
|
+
_globals['_STREAMLOGSREQUEST']._serialized_end=478
|
|
39
|
+
_globals['_STREAMLOGSRESPONSE']._serialized_start=480
|
|
40
|
+
_globals['_STREAMLOGSRESPONSE']._serialized_end=520
|
|
41
|
+
_globals['_EXEC']._serialized_start=523
|
|
42
|
+
_globals['_EXEC']._serialized_end=683
|
|
39
43
|
# @@protoc_insertion_point(module_scope)
|
flwr/proto/exec_pb2.pyi
CHANGED
|
@@ -29,17 +29,36 @@ class StartRunRequest(google.protobuf.message.Message):
|
|
|
29
29
|
def HasField(self, field_name: typing_extensions.Literal["value",b"value"]) -> builtins.bool: ...
|
|
30
30
|
def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
|
|
31
31
|
|
|
32
|
+
class FederationConfigEntry(google.protobuf.message.Message):
|
|
33
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
34
|
+
KEY_FIELD_NUMBER: builtins.int
|
|
35
|
+
VALUE_FIELD_NUMBER: builtins.int
|
|
36
|
+
key: typing.Text
|
|
37
|
+
@property
|
|
38
|
+
def value(self) -> flwr.proto.transport_pb2.Scalar: ...
|
|
39
|
+
def __init__(self,
|
|
40
|
+
*,
|
|
41
|
+
key: typing.Text = ...,
|
|
42
|
+
value: typing.Optional[flwr.proto.transport_pb2.Scalar] = ...,
|
|
43
|
+
) -> None: ...
|
|
44
|
+
def HasField(self, field_name: typing_extensions.Literal["value",b"value"]) -> builtins.bool: ...
|
|
45
|
+
def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
|
|
46
|
+
|
|
32
47
|
FAB_FILE_FIELD_NUMBER: builtins.int
|
|
33
48
|
OVERRIDE_CONFIG_FIELD_NUMBER: builtins.int
|
|
49
|
+
FEDERATION_CONFIG_FIELD_NUMBER: builtins.int
|
|
34
50
|
fab_file: builtins.bytes
|
|
35
51
|
@property
|
|
36
52
|
def override_config(self) -> google.protobuf.internal.containers.MessageMap[typing.Text, flwr.proto.transport_pb2.Scalar]: ...
|
|
53
|
+
@property
|
|
54
|
+
def federation_config(self) -> google.protobuf.internal.containers.MessageMap[typing.Text, flwr.proto.transport_pb2.Scalar]: ...
|
|
37
55
|
def __init__(self,
|
|
38
56
|
*,
|
|
39
57
|
fab_file: builtins.bytes = ...,
|
|
40
58
|
override_config: typing.Optional[typing.Mapping[typing.Text, flwr.proto.transport_pb2.Scalar]] = ...,
|
|
59
|
+
federation_config: typing.Optional[typing.Mapping[typing.Text, flwr.proto.transport_pb2.Scalar]] = ...,
|
|
41
60
|
) -> None: ...
|
|
42
|
-
def ClearField(self, field_name: typing_extensions.Literal["fab_file",b"fab_file","override_config",b"override_config"]) -> None: ...
|
|
61
|
+
def ClearField(self, field_name: typing_extensions.Literal["fab_file",b"fab_file","federation_config",b"federation_config","override_config",b"override_config"]) -> None: ...
|
|
43
62
|
global___StartRunRequest = StartRunRequest
|
|
44
63
|
|
|
45
64
|
class StartRunResponse(google.protobuf.message.Message):
|
flwr/server/run_serverapp.py
CHANGED
|
@@ -72,8 +72,8 @@ def _register_node_states(
|
|
|
72
72
|
node_states[node_id] = NodeState(
|
|
73
73
|
node_id=node_id,
|
|
74
74
|
node_config={
|
|
75
|
-
PARTITION_ID_KEY:
|
|
76
|
-
NUM_PARTITIONS_KEY:
|
|
75
|
+
PARTITION_ID_KEY: partition_id,
|
|
76
|
+
NUM_PARTITIONS_KEY: num_partitions,
|
|
77
77
|
},
|
|
78
78
|
)
|
|
79
79
|
|
|
@@ -347,8 +347,8 @@ def start_vce(
|
|
|
347
347
|
if client_app_attr:
|
|
348
348
|
app = _get_load_client_app_fn(
|
|
349
349
|
default_app_ref=client_app_attr,
|
|
350
|
-
|
|
351
|
-
|
|
350
|
+
project_dir=app_dir,
|
|
351
|
+
flwr_dir=flwr_dir,
|
|
352
352
|
multi_app=True,
|
|
353
353
|
)(run.fab_id, run.fab_version)
|
|
354
354
|
|
flwr/simulation/__init__.py
CHANGED