flwr-nightly 1.15.0.dev20250122__py3-none-any.whl → 1.15.0.dev20250124__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flwr/cli/config_utils.py +16 -3
- flwr/cli/constant.py +27 -0
- flwr/cli/log.py +18 -3
- flwr/cli/login/login.py +24 -2
- flwr/cli/ls.py +11 -3
- flwr/cli/run/run.py +21 -11
- flwr/cli/stop.py +10 -2
- flwr/cli/utils.py +29 -12
- flwr/common/config.py +21 -10
- flwr/server/driver/grpc_driver.py +22 -21
- {flwr_nightly-1.15.0.dev20250122.dist-info → flwr_nightly-1.15.0.dev20250124.dist-info}/METADATA +1 -1
- {flwr_nightly-1.15.0.dev20250122.dist-info → flwr_nightly-1.15.0.dev20250124.dist-info}/RECORD +15 -14
- {flwr_nightly-1.15.0.dev20250122.dist-info → flwr_nightly-1.15.0.dev20250124.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.15.0.dev20250122.dist-info → flwr_nightly-1.15.0.dev20250124.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.15.0.dev20250122.dist-info → flwr_nightly-1.15.0.dev20250124.dist-info}/entry_points.txt +0 -0
flwr/cli/config_utils.py
CHANGED
@@ -21,7 +21,13 @@ from typing import Any, Optional, Union
|
|
21
21
|
import tomli
|
22
22
|
import typer
|
23
23
|
|
24
|
-
from flwr.common.config import
|
24
|
+
from flwr.common.config import (
|
25
|
+
fuse_dicts,
|
26
|
+
get_fab_config,
|
27
|
+
get_metadata_from_config,
|
28
|
+
parse_config_args,
|
29
|
+
validate_config,
|
30
|
+
)
|
25
31
|
|
26
32
|
|
27
33
|
def get_fab_metadata(fab_file: Union[Path, bytes]) -> tuple[str, str]:
|
@@ -127,7 +133,9 @@ def process_loaded_project_config(
|
|
127
133
|
|
128
134
|
|
129
135
|
def validate_federation_in_project_config(
|
130
|
-
federation: Optional[str],
|
136
|
+
federation: Optional[str],
|
137
|
+
config: dict[str, Any],
|
138
|
+
overrides: Optional[list[str]] = None,
|
131
139
|
) -> tuple[str, dict[str, Any]]:
|
132
140
|
"""Validate the federation name in the Flower project configuration."""
|
133
141
|
federation = federation or config["tool"]["flwr"]["federations"].get("default")
|
@@ -157,6 +165,11 @@ def validate_federation_in_project_config(
|
|
157
165
|
)
|
158
166
|
raise typer.Exit(code=1)
|
159
167
|
|
168
|
+
# Override the federation configuration if provided
|
169
|
+
if overrides:
|
170
|
+
overrides_dict = parse_config_args(overrides, flatten=False)
|
171
|
+
federation_config = fuse_dicts(federation_config, overrides_dict)
|
172
|
+
|
160
173
|
return federation, federation_config
|
161
174
|
|
162
175
|
|
@@ -169,7 +182,7 @@ def validate_certificate_in_federation_config(
|
|
169
182
|
root_certificates_bytes = (app / root_certificates).read_bytes()
|
170
183
|
if insecure := bool(insecure_str):
|
171
184
|
typer.secho(
|
172
|
-
"❌ `
|
185
|
+
"❌ `root-certificates` were provided but the `insecure` parameter "
|
173
186
|
"is set to `True`.",
|
174
187
|
fg=typer.colors.RED,
|
175
188
|
bold=True,
|
flwr/cli/constant.py
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
# ==============================================================================
|
15
|
+
"""Constants for CLI commands."""
|
16
|
+
|
17
|
+
|
18
|
+
# The help message for `--federation-config` option
|
19
|
+
FEDERATION_CONFIG_HELP_MESSAGE = (
|
20
|
+
"Override federation configuration values in the format:\n\n"
|
21
|
+
"`--federation-config 'key1=value1 key2=value2' --federation-config "
|
22
|
+
"'key3=value3'`\n\nValues can be of any type supported in TOML, such as "
|
23
|
+
"bool, int, float, or string. Ensure that the keys (`key1`, `key2`, `key3` "
|
24
|
+
"in this example) exist in the federation configuration under the "
|
25
|
+
"`[tool.flwr.federations.<YOUR_FEDERATION>]` table of the `pyproject.toml` "
|
26
|
+
"for proper overriding."
|
27
|
+
)
|
flwr/cli/log.py
CHANGED
@@ -29,6 +29,7 @@ from flwr.cli.config_utils import (
|
|
29
29
|
process_loaded_project_config,
|
30
30
|
validate_federation_in_project_config,
|
31
31
|
)
|
32
|
+
from flwr.cli.constant import FEDERATION_CONFIG_HELP_MESSAGE
|
32
33
|
from flwr.common.constant import CONN_RECONNECT_INTERVAL, CONN_REFRESH_PERIOD
|
33
34
|
from flwr.common.logger import log as logger
|
34
35
|
from flwr.proto.exec_pb2 import StreamLogsRequest # pylint: disable=E0611
|
@@ -57,6 +58,8 @@ def start_stream(
|
|
57
58
|
logger(ERROR, "Invalid run_id `%s`, exiting", run_id)
|
58
59
|
if e.code() == grpc.StatusCode.CANCELLED:
|
59
60
|
pass
|
61
|
+
else:
|
62
|
+
raise e
|
60
63
|
finally:
|
61
64
|
channel.close()
|
62
65
|
|
@@ -123,6 +126,7 @@ def print_logs(run_id: int, channel: grpc.Channel, timeout: int) -> None:
|
|
123
126
|
break
|
124
127
|
if e.code() == grpc.StatusCode.CANCELLED:
|
125
128
|
break
|
129
|
+
raise e
|
126
130
|
except KeyboardInterrupt:
|
127
131
|
logger(DEBUG, "Stream interrupted by user")
|
128
132
|
finally:
|
@@ -143,6 +147,13 @@ def log(
|
|
143
147
|
Optional[str],
|
144
148
|
typer.Argument(help="Name of the federation to run the app on"),
|
145
149
|
] = None,
|
150
|
+
federation_config_overrides: Annotated[
|
151
|
+
Optional[list[str]],
|
152
|
+
typer.Option(
|
153
|
+
"--federation-config",
|
154
|
+
help=FEDERATION_CONFIG_HELP_MESSAGE,
|
155
|
+
),
|
156
|
+
] = None,
|
146
157
|
stream: Annotated[
|
147
158
|
bool,
|
148
159
|
typer.Option(
|
@@ -158,11 +169,15 @@ def log(
|
|
158
169
|
config, errors, warnings = load_and_validate(path=pyproject_path)
|
159
170
|
config = process_loaded_project_config(config, errors, warnings)
|
160
171
|
federation, federation_config = validate_federation_in_project_config(
|
161
|
-
federation, config
|
172
|
+
federation, config, federation_config_overrides
|
162
173
|
)
|
163
174
|
exit_if_no_address(federation_config, "log")
|
164
175
|
|
165
|
-
|
176
|
+
try:
|
177
|
+
_log_with_exec_api(app, federation, federation_config, run_id, stream)
|
178
|
+
except Exception as err: # pylint: disable=broad-except
|
179
|
+
typer.secho(str(err), fg=typer.colors.RED, bold=True)
|
180
|
+
raise typer.Exit(code=1) from None
|
166
181
|
|
167
182
|
|
168
183
|
def _log_with_exec_api(
|
@@ -172,7 +187,7 @@ def _log_with_exec_api(
|
|
172
187
|
run_id: int,
|
173
188
|
stream: bool,
|
174
189
|
) -> None:
|
175
|
-
auth_plugin = try_obtain_cli_auth_plugin(app, federation)
|
190
|
+
auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
|
176
191
|
channel = init_channel(app, federation_config, auth_plugin)
|
177
192
|
|
178
193
|
if stream:
|
flwr/cli/login/login.py
CHANGED
@@ -26,6 +26,7 @@ from flwr.cli.config_utils import (
|
|
26
26
|
process_loaded_project_config,
|
27
27
|
validate_federation_in_project_config,
|
28
28
|
)
|
29
|
+
from flwr.cli.constant import FEDERATION_CONFIG_HELP_MESSAGE
|
29
30
|
from flwr.common.typing import UserAuthLoginDetails
|
30
31
|
from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
31
32
|
GetLoginDetailsRequest,
|
@@ -45,6 +46,13 @@ def login( # pylint: disable=R0914
|
|
45
46
|
Optional[str],
|
46
47
|
typer.Argument(help="Name of the federation to login into."),
|
47
48
|
] = None,
|
49
|
+
federation_config_overrides: Annotated[
|
50
|
+
Optional[list[str]],
|
51
|
+
typer.Option(
|
52
|
+
"--federation-config",
|
53
|
+
help=FEDERATION_CONFIG_HELP_MESSAGE,
|
54
|
+
),
|
55
|
+
] = None,
|
48
56
|
) -> None:
|
49
57
|
"""Login to Flower SuperLink."""
|
50
58
|
typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
|
@@ -54,9 +62,21 @@ def login( # pylint: disable=R0914
|
|
54
62
|
|
55
63
|
config = process_loaded_project_config(config, errors, warnings)
|
56
64
|
federation, federation_config = validate_federation_in_project_config(
|
57
|
-
federation, config
|
65
|
+
federation, config, federation_config_overrides
|
58
66
|
)
|
59
67
|
exit_if_no_address(federation_config, "login")
|
68
|
+
|
69
|
+
# Check if `enable-user-auth` is set to `true`
|
70
|
+
if not federation_config.get("enable-user-auth", False):
|
71
|
+
typer.secho(
|
72
|
+
f"❌ User authentication is not enabled for the federation '{federation}'. "
|
73
|
+
"To enable it, set `enable-user-auth = true` in the federation "
|
74
|
+
"configuration.",
|
75
|
+
fg=typer.colors.RED,
|
76
|
+
bold=True,
|
77
|
+
)
|
78
|
+
raise typer.Exit(code=1)
|
79
|
+
|
60
80
|
channel = init_channel(app, federation_config, None)
|
61
81
|
stub = ExecStub(channel)
|
62
82
|
|
@@ -65,7 +85,9 @@ def login( # pylint: disable=R0914
|
|
65
85
|
|
66
86
|
# Get the auth plugin
|
67
87
|
auth_type = login_response.auth_type
|
68
|
-
auth_plugin = try_obtain_cli_auth_plugin(
|
88
|
+
auth_plugin = try_obtain_cli_auth_plugin(
|
89
|
+
app, federation, federation_config, auth_type
|
90
|
+
)
|
69
91
|
if auth_plugin is None:
|
70
92
|
typer.secho(
|
71
93
|
f'❌ Authentication type "{auth_type}" not found',
|
flwr/cli/ls.py
CHANGED
@@ -32,6 +32,7 @@ from flwr.cli.config_utils import (
|
|
32
32
|
process_loaded_project_config,
|
33
33
|
validate_federation_in_project_config,
|
34
34
|
)
|
35
|
+
from flwr.cli.constant import FEDERATION_CONFIG_HELP_MESSAGE
|
35
36
|
from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat, SubStatus
|
36
37
|
from flwr.common.date import format_timedelta, isoformat8601_utc
|
37
38
|
from flwr.common.logger import print_json_error, redirect_output, restore_output
|
@@ -48,7 +49,7 @@ from .utils import init_channel, try_obtain_cli_auth_plugin, unauthenticated_exc
|
|
48
49
|
_RunListType = tuple[int, str, str, str, str, str, str, str, str]
|
49
50
|
|
50
51
|
|
51
|
-
def ls( # pylint: disable=too-many-locals, too-many-branches
|
52
|
+
def ls( # pylint: disable=too-many-locals, too-many-branches, R0913, R0917
|
52
53
|
app: Annotated[
|
53
54
|
Path,
|
54
55
|
typer.Argument(help="Path of the Flower project"),
|
@@ -57,6 +58,13 @@ def ls( # pylint: disable=too-many-locals, too-many-branches
|
|
57
58
|
Optional[str],
|
58
59
|
typer.Argument(help="Name of the federation"),
|
59
60
|
] = None,
|
61
|
+
federation_config_overrides: Annotated[
|
62
|
+
Optional[list[str]],
|
63
|
+
typer.Option(
|
64
|
+
"--federation-config",
|
65
|
+
help=FEDERATION_CONFIG_HELP_MESSAGE,
|
66
|
+
),
|
67
|
+
] = None,
|
60
68
|
runs: Annotated[
|
61
69
|
bool,
|
62
70
|
typer.Option(
|
@@ -106,7 +114,7 @@ def ls( # pylint: disable=too-many-locals, too-many-branches
|
|
106
114
|
config, errors, warnings = load_and_validate(path=pyproject_path)
|
107
115
|
config = process_loaded_project_config(config, errors, warnings)
|
108
116
|
federation, federation_config = validate_federation_in_project_config(
|
109
|
-
federation, config
|
117
|
+
federation, config, federation_config_overrides
|
110
118
|
)
|
111
119
|
exit_if_no_address(federation_config, "ls")
|
112
120
|
|
@@ -115,7 +123,7 @@ def ls( # pylint: disable=too-many-locals, too-many-branches
|
|
115
123
|
raise ValueError(
|
116
124
|
"The options '--runs' and '--run-id' are mutually exclusive."
|
117
125
|
)
|
118
|
-
auth_plugin = try_obtain_cli_auth_plugin(app, federation)
|
126
|
+
auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
|
119
127
|
channel = init_channel(app, federation_config, auth_plugin)
|
120
128
|
stub = ExecStub(channel)
|
121
129
|
|
flwr/cli/run/run.py
CHANGED
@@ -31,6 +31,7 @@ from flwr.cli.config_utils import (
|
|
31
31
|
process_loaded_project_config,
|
32
32
|
validate_federation_in_project_config,
|
33
33
|
)
|
34
|
+
from flwr.cli.constant import FEDERATION_CONFIG_HELP_MESSAGE
|
34
35
|
from flwr.common.config import (
|
35
36
|
flatten_dict,
|
36
37
|
parse_config_args,
|
@@ -57,7 +58,7 @@ from ..utils import (
|
|
57
58
|
CONN_REFRESH_PERIOD = 60 # Connection refresh period for log streaming (seconds)
|
58
59
|
|
59
60
|
|
60
|
-
# pylint: disable-next=too-many-locals
|
61
|
+
# pylint: disable-next=too-many-locals, R0913, R0917
|
61
62
|
def run(
|
62
63
|
app: Annotated[
|
63
64
|
Path,
|
@@ -67,16 +68,23 @@ def run(
|
|
67
68
|
Optional[str],
|
68
69
|
typer.Argument(help="Name of the federation to run the app on."),
|
69
70
|
] = None,
|
70
|
-
|
71
|
+
run_config_overrides: Annotated[
|
71
72
|
Optional[list[str]],
|
72
73
|
typer.Option(
|
73
74
|
"--run-config",
|
74
75
|
"-c",
|
75
|
-
help="Override configuration
|
76
|
-
|
77
|
-
"
|
78
|
-
"
|
79
|
-
"
|
76
|
+
help="Override run configuration values in the format:\n\n"
|
77
|
+
"`--run-config 'key1=value1 key2=value2' --run-config 'key3=value3'`\n\n"
|
78
|
+
"Values can be of any type supported in TOML, such as bool, int, "
|
79
|
+
"float, or string. Ensure that the keys (`key1`, `key2`, `key3` "
|
80
|
+
"in this example) exist in `pyproject.toml` for proper overriding.",
|
81
|
+
),
|
82
|
+
] = None,
|
83
|
+
federation_config_overrides: Annotated[
|
84
|
+
Optional[list[str]],
|
85
|
+
typer.Option(
|
86
|
+
"--federation-config",
|
87
|
+
help=FEDERATION_CONFIG_HELP_MESSAGE,
|
80
88
|
),
|
81
89
|
] = None,
|
82
90
|
stream: Annotated[
|
@@ -108,7 +116,7 @@ def run(
|
|
108
116
|
config, errors, warnings = load_and_validate(path=pyproject_path)
|
109
117
|
config = process_loaded_project_config(config, errors, warnings)
|
110
118
|
federation, federation_config = validate_federation_in_project_config(
|
111
|
-
federation, config
|
119
|
+
federation, config, federation_config_overrides
|
112
120
|
)
|
113
121
|
|
114
122
|
if "address" in federation_config:
|
@@ -116,12 +124,14 @@ def run(
|
|
116
124
|
app,
|
117
125
|
federation,
|
118
126
|
federation_config,
|
119
|
-
|
127
|
+
run_config_overrides,
|
120
128
|
stream,
|
121
129
|
output_format,
|
122
130
|
)
|
123
131
|
else:
|
124
|
-
_run_without_exec_api(
|
132
|
+
_run_without_exec_api(
|
133
|
+
app, federation_config, run_config_overrides, federation
|
134
|
+
)
|
125
135
|
except (typer.Exit, Exception) as err: # pylint: disable=broad-except
|
126
136
|
if suppress_output:
|
127
137
|
restore_output()
|
@@ -148,7 +158,7 @@ def _run_with_exec_api(
|
|
148
158
|
stream: bool,
|
149
159
|
output_format: str,
|
150
160
|
) -> None:
|
151
|
-
auth_plugin = try_obtain_cli_auth_plugin(app, federation)
|
161
|
+
auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
|
152
162
|
channel = init_channel(app, federation_config, auth_plugin)
|
153
163
|
stub = ExecStub(channel)
|
154
164
|
|
flwr/cli/stop.py
CHANGED
@@ -29,6 +29,7 @@ from flwr.cli.config_utils import (
|
|
29
29
|
process_loaded_project_config,
|
30
30
|
validate_federation_in_project_config,
|
31
31
|
)
|
32
|
+
from flwr.cli.constant import FEDERATION_CONFIG_HELP_MESSAGE
|
32
33
|
from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat
|
33
34
|
from flwr.common.logger import print_json_error, redirect_output, restore_output
|
34
35
|
from flwr.proto.exec_pb2 import StopRunRequest, StopRunResponse # pylint: disable=E0611
|
@@ -50,6 +51,13 @@ def stop( # pylint: disable=R0914
|
|
50
51
|
Optional[str],
|
51
52
|
typer.Argument(help="Name of the federation"),
|
52
53
|
] = None,
|
54
|
+
federation_config_overrides: Annotated[
|
55
|
+
Optional[list[str]],
|
56
|
+
typer.Option(
|
57
|
+
"--federation-config",
|
58
|
+
help=FEDERATION_CONFIG_HELP_MESSAGE,
|
59
|
+
),
|
60
|
+
] = None,
|
53
61
|
output_format: Annotated[
|
54
62
|
str,
|
55
63
|
typer.Option(
|
@@ -73,12 +81,12 @@ def stop( # pylint: disable=R0914
|
|
73
81
|
config, errors, warnings = load_and_validate(path=pyproject_path)
|
74
82
|
config = process_loaded_project_config(config, errors, warnings)
|
75
83
|
federation, federation_config = validate_federation_in_project_config(
|
76
|
-
federation, config
|
84
|
+
federation, config, federation_config_overrides
|
77
85
|
)
|
78
86
|
exit_if_no_address(federation_config, "stop")
|
79
87
|
|
80
88
|
try:
|
81
|
-
auth_plugin = try_obtain_cli_auth_plugin(app, federation)
|
89
|
+
auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
|
82
90
|
channel = init_channel(app, federation_config, auth_plugin)
|
83
91
|
stub = ExecStub(channel) # pylint: disable=unused-variable # noqa: F841
|
84
92
|
|
flwr/cli/utils.py
CHANGED
@@ -217,25 +217,42 @@ def get_user_auth_config_path(root_dir: Path, federation: str) -> Path:
|
|
217
217
|
def try_obtain_cli_auth_plugin(
|
218
218
|
root_dir: Path,
|
219
219
|
federation: str,
|
220
|
+
federation_config: dict[str, Any],
|
220
221
|
auth_type: Optional[str] = None,
|
221
222
|
) -> Optional[CliAuthPlugin]:
|
222
223
|
"""Load the CLI-side user auth plugin for the given auth type."""
|
223
|
-
|
224
|
-
|
225
|
-
# Load the config file if it exists
|
226
|
-
json_file: dict[str, Any] = {}
|
227
|
-
if config_path.exists():
|
228
|
-
with config_path.open("r", encoding="utf-8") as file:
|
229
|
-
json_file = json.load(file)
|
230
|
-
# This is the case when the user auth is not enabled
|
231
|
-
elif auth_type is None:
|
224
|
+
# Check if user auth is enabled
|
225
|
+
if not federation_config.get("enable-user-auth", False):
|
232
226
|
return None
|
233
227
|
|
228
|
+
# Check if TLS is enabled. If not, raise an error
|
229
|
+
if federation_config.get("root-certificates") is None:
|
230
|
+
typer.secho(
|
231
|
+
"❌ User authentication requires TLS to be enabled. "
|
232
|
+
"Please provide 'root-certificates' in the federation"
|
233
|
+
" configuration.",
|
234
|
+
fg=typer.colors.RED,
|
235
|
+
bold=True,
|
236
|
+
)
|
237
|
+
raise typer.Exit(code=1)
|
238
|
+
|
239
|
+
config_path = get_user_auth_config_path(root_dir, federation)
|
240
|
+
|
234
241
|
# Get the auth type from the config if not provided
|
242
|
+
# auth_type will be None for all CLI commands except login
|
235
243
|
if auth_type is None:
|
236
|
-
|
237
|
-
|
238
|
-
|
244
|
+
try:
|
245
|
+
with config_path.open("r", encoding="utf-8") as file:
|
246
|
+
json_file = json.load(file)
|
247
|
+
auth_type = json_file[AUTH_TYPE]
|
248
|
+
except (FileNotFoundError, KeyError):
|
249
|
+
typer.secho(
|
250
|
+
"❌ Missing or invalid credentials for user authentication. "
|
251
|
+
"Please run `flwr login` to authenticate.",
|
252
|
+
fg=typer.colors.RED,
|
253
|
+
bold=True,
|
254
|
+
)
|
255
|
+
raise typer.Exit(code=1) from None
|
239
256
|
|
240
257
|
# Retrieve auth plugin class and instantiate it
|
241
258
|
try:
|
flwr/common/config.py
CHANGED
@@ -20,7 +20,7 @@ import re
|
|
20
20
|
import zipfile
|
21
21
|
from io import BytesIO
|
22
22
|
from pathlib import Path
|
23
|
-
from typing import IO, Any, Optional, Union, cast, get_args
|
23
|
+
from typing import IO, Any, Optional, TypeVar, Union, cast, get_args
|
24
24
|
|
25
25
|
import tomli
|
26
26
|
|
@@ -35,6 +35,8 @@ from flwr.common.typing import Run, UserConfig, UserConfigValue
|
|
35
35
|
|
36
36
|
from . import ConfigsRecord, object_ref
|
37
37
|
|
38
|
+
T_dict = TypeVar("T_dict", bound=dict[str, Any]) # pylint: disable=invalid-name
|
39
|
+
|
38
40
|
|
39
41
|
def get_flwr_dir(provided_path: Optional[str] = None) -> Path:
|
40
42
|
"""Return the Flower home directory based on env variables."""
|
@@ -93,19 +95,28 @@ def get_project_config(project_dir: Union[str, Path]) -> dict[str, Any]:
|
|
93
95
|
|
94
96
|
|
95
97
|
def fuse_dicts(
|
96
|
-
main_dict:
|
97
|
-
override_dict:
|
98
|
-
|
98
|
+
main_dict: T_dict,
|
99
|
+
override_dict: T_dict,
|
100
|
+
check_keys: bool = True,
|
101
|
+
) -> T_dict:
|
99
102
|
"""Merge a config with the overrides.
|
100
103
|
|
101
|
-
|
102
|
-
|
104
|
+
If `check_keys` is set to True, an error will be raised if the override
|
105
|
+
dictionary contains keys that are not present in the main dictionary.
|
106
|
+
Otherwise, only the keys present in the main dictionary will be updated.
|
103
107
|
"""
|
104
|
-
|
108
|
+
if not isinstance(main_dict, dict) or not isinstance(override_dict, dict):
|
109
|
+
raise ValueError("Both dictionaries must be of type dict")
|
110
|
+
|
111
|
+
fused_dict = cast(T_dict, main_dict.copy())
|
105
112
|
|
106
113
|
for key, value in override_dict.items():
|
107
114
|
if key in main_dict:
|
115
|
+
if isinstance(value, dict):
|
116
|
+
fused_dict[key] = fuse_dicts(main_dict[key], value)
|
108
117
|
fused_dict[key] = value
|
118
|
+
elif check_keys:
|
119
|
+
raise ValueError(f"Key '{key}' is not present in the main dictionary")
|
109
120
|
|
110
121
|
return fused_dict
|
111
122
|
|
@@ -194,8 +205,8 @@ def unflatten_dict(flat_dict: dict[str, Any]) -> dict[str, Any]:
|
|
194
205
|
|
195
206
|
|
196
207
|
def parse_config_args(
|
197
|
-
config: Optional[list[str]],
|
198
|
-
) ->
|
208
|
+
config: Optional[list[str]], flatten: bool = True
|
209
|
+
) -> dict[str, Any]:
|
199
210
|
"""Parse separator separated list of key-value pairs separated by '='."""
|
200
211
|
overrides: UserConfig = {}
|
201
212
|
|
@@ -223,7 +234,7 @@ def parse_config_args(
|
|
223
234
|
matches = pattern.findall(config_line)
|
224
235
|
toml_str = "\n".join(f"{k} = {v}" for k, v in matches)
|
225
236
|
overrides.update(tomli.loads(toml_str))
|
226
|
-
flat_overrides = flatten_dict(overrides)
|
237
|
+
flat_overrides = flatten_dict(overrides) if flatten else overrides
|
227
238
|
|
228
239
|
return flat_overrides
|
229
240
|
|
@@ -31,20 +31,20 @@ from flwr.common.constant import (
|
|
31
31
|
from flwr.common.grpc import create_channel
|
32
32
|
from flwr.common.logger import log
|
33
33
|
from flwr.common.retry_invoker import _make_simple_grpc_retry_invoker, _wrap_stub
|
34
|
-
from flwr.common.serde import
|
34
|
+
from flwr.common.serde import message_from_proto, message_to_proto, run_from_proto
|
35
35
|
from flwr.common.typing import Run
|
36
|
+
from flwr.proto.message_pb2 import Message as ProtoMessage # pylint: disable=E0611
|
36
37
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
37
38
|
from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
|
38
39
|
from flwr.proto.serverappio_pb2 import ( # pylint: disable=E0611
|
39
40
|
GetNodesRequest,
|
40
41
|
GetNodesResponse,
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
PullResMessagesRequest,
|
43
|
+
PullResMessagesResponse,
|
44
|
+
PushInsMessagesRequest,
|
45
|
+
PushInsMessagesResponse,
|
45
46
|
)
|
46
47
|
from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub # pylint: disable=E0611
|
47
|
-
from flwr.proto.task_pb2 import TaskIns # pylint: disable=E0611
|
48
48
|
|
49
49
|
from .driver import Driver
|
50
50
|
|
@@ -196,22 +196,22 @@ class GrpcDriver(Driver):
|
|
196
196
|
This method takes an iterable of messages and sends each message
|
197
197
|
to the node specified in `dst_node_id`.
|
198
198
|
"""
|
199
|
-
# Construct
|
200
|
-
|
199
|
+
# Construct Messages
|
200
|
+
message_proto_list: list[ProtoMessage] = []
|
201
201
|
for msg in messages:
|
202
202
|
# Check message
|
203
203
|
self._check_message(msg)
|
204
|
-
# Convert
|
205
|
-
|
204
|
+
# Convert to proto
|
205
|
+
msg_proto = message_to_proto(msg)
|
206
206
|
# Add to list
|
207
|
-
|
207
|
+
message_proto_list.append(msg_proto)
|
208
208
|
# Call GrpcDriverStub method
|
209
|
-
res:
|
210
|
-
|
211
|
-
|
209
|
+
res: PushInsMessagesResponse = self._stub.PushMessages(
|
210
|
+
PushInsMessagesRequest(
|
211
|
+
messages_list=message_proto_list, run_id=cast(Run, self._run).run_id
|
212
212
|
)
|
213
213
|
)
|
214
|
-
return list(res.
|
214
|
+
return list(res.message_ids)
|
215
215
|
|
216
216
|
def pull_messages(self, message_ids: Iterable[str]) -> Iterable[Message]:
|
217
217
|
"""Pull messages based on message IDs.
|
@@ -219,14 +219,15 @@ class GrpcDriver(Driver):
|
|
219
219
|
This method is used to collect messages from the SuperLink that correspond to a
|
220
220
|
set of given message IDs.
|
221
221
|
"""
|
222
|
-
# Pull
|
223
|
-
res:
|
224
|
-
|
225
|
-
|
222
|
+
# Pull Messages
|
223
|
+
res: PullResMessagesResponse = self._stub.PullMessages(
|
224
|
+
PullResMessagesRequest(
|
225
|
+
message_ids=message_ids,
|
226
|
+
run_id=cast(Run, self._run).run_id,
|
226
227
|
)
|
227
228
|
)
|
228
|
-
# Convert
|
229
|
-
msgs = [
|
229
|
+
# Convert Message from Protobuf representation
|
230
|
+
msgs = [message_from_proto(msg_proto) for msg_proto in res.messages_list]
|
230
231
|
return msgs
|
231
232
|
|
232
233
|
def send_and_receive(
|
{flwr_nightly-1.15.0.dev20250122.dist-info → flwr_nightly-1.15.0.dev20250124.dist-info}/RECORD
RENAMED
@@ -3,13 +3,14 @@ flwr/cli/__init__.py,sha256=cZJVgozlkC6Ni2Hd_FAIrqefrkCGOV18fikToq-6iLw,720
|
|
3
3
|
flwr/cli/app.py,sha256=UeXrW5gxrUnFViDjAMIxGNZZKwu3a1oAj83v53IWIWM,1382
|
4
4
|
flwr/cli/build.py,sha256=4P70i_FnUs0P21aTwjTXtFQSAfY-C04hUDF-2npfJdo,6345
|
5
5
|
flwr/cli/cli_user_auth_interceptor.py,sha256=aZepPA298s-HjGmkJGMvI_uZe72O5aLC3jri-ilG53o,3126
|
6
|
-
flwr/cli/config_utils.py,sha256=
|
6
|
+
flwr/cli/config_utils.py,sha256=LelRR960I36n1IPw7BIu79fKoOh0JePA58kAtoXSTH0,7518
|
7
|
+
flwr/cli/constant.py,sha256=g7Ad7o3DJDkJNrWS0T3SSJETWSTkkVJWGpLM8zlbpcY,1289
|
7
8
|
flwr/cli/example.py,sha256=uk5CoD0ZITgpY_ffsTbEKf8XOOCSUzByjHPcMSPqV18,2216
|
8
9
|
flwr/cli/install.py,sha256=-RnrYGejN_zyXXp_CoddSQwoQfRTWWyt9WYlxphJzyU,8180
|
9
|
-
flwr/cli/log.py,sha256=
|
10
|
+
flwr/cli/log.py,sha256=vcO-r5EIc127mOQ26uxKVITX-w_Zib7AxSVuuN70_JY,6671
|
10
11
|
flwr/cli/login/__init__.py,sha256=6_9zOzbPOAH72K2wX3-9dXTAbS7Mjpa5sEn2lA6eHHI,800
|
11
|
-
flwr/cli/login/login.py,sha256=
|
12
|
-
flwr/cli/ls.py,sha256=
|
12
|
+
flwr/cli/login/login.py,sha256=EOU1E-89VPn5Ns7ytaDdGNVVeOoC4ULFjtNPogjXGGQ,3783
|
13
|
+
flwr/cli/ls.py,sha256=5KCHdctN5f5GkCAkbZSC1OuKdhmLzobINpltsdtDtQU,11383
|
13
14
|
flwr/cli/new/__init__.py,sha256=pOQtPT9W4kCIttcKne5m-FtJbvTqdjTVJxzQ9AUYK8I,790
|
14
15
|
flwr/cli/new/new.py,sha256=scyyKt8mzkc3El1bypgkHjKwVQEc2-q4I50PxriPFdI,9922
|
15
16
|
flwr/cli/new/templates/__init__.py,sha256=4luU8RL-CK8JJCstQ_ON809W9bNTkY1l9zSaPKBkgwY,725
|
@@ -65,9 +66,9 @@ flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=PA_dEtWkY6Wf3Ektc9D
|
|
65
66
|
flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=d7TwWH-QJN0j-9rs9zHnng_bHeHV7mk138KXqdv5BoE,686
|
66
67
|
flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=r0SZnvoR5a5mEWKJIvQ3rW8C6-2DkhaC8A1fTA0avm0,710
|
67
68
|
flwr/cli/run/__init__.py,sha256=cCsKVB0SFzh2b3QmGba6BHckB85xlhjh3mh4pBpACtY,790
|
68
|
-
flwr/cli/run/run.py,sha256=
|
69
|
-
flwr/cli/stop.py,sha256=
|
70
|
-
flwr/cli/utils.py,sha256=
|
69
|
+
flwr/cli/run/run.py,sha256=kEOYKin9qPJy8SODxcAvIWk-OskKPsxvcbvhDhf2VD4,8299
|
70
|
+
flwr/cli/stop.py,sha256=DBCKg9AhB1WcJsyqfkHKR1_V_yT7D32zqa9QhmX9IAU,4926
|
71
|
+
flwr/cli/utils.py,sha256=z5WFbh2RwMirFcv6QtV_4rJlK7ad2XrUJUsinNhqE28,11310
|
71
72
|
flwr/client/__init__.py,sha256=DGDoO0AEAfz-0CUFmLdyUUweAS64-07AOnmDfWUefK4,1192
|
72
73
|
flwr/client/app.py,sha256=tNnef5wGVfqMiiGiWzAuULyy1QpvCKukiRmNi_a2cQc,34261
|
73
74
|
flwr/client/client.py,sha256=8o58nd9o6ZFcMIaVYPGcV4MSjBG4H0oFgWiv8ZEO3oA,7895
|
@@ -113,7 +114,7 @@ flwr/common/address.py,sha256=9KNYE69WW_QVcyumsux3Qn1wmn4J7f13Y9nHASpvzbA,3018
|
|
113
114
|
flwr/common/args.py,sha256=bCvtG0hhh_hVjl9NoWsY_g7kLMIN3jCN7B883HvZ7hg,6223
|
114
115
|
flwr/common/auth_plugin/__init__.py,sha256=1Y8Oj3iB49IHDu9tvDih1J74Ygu7k85V9s2A4WORPyA,887
|
115
116
|
flwr/common/auth_plugin/auth_plugin.py,sha256=wgDorBUB4IkK6twQ8vNawRVz7BDPmKdXZBNLqhU9RSs,3871
|
116
|
-
flwr/common/config.py,sha256=
|
117
|
+
flwr/common/config.py,sha256=n6T5Vi6BUFul37GUpKp9Doqnz35phJqSud_G3ySWlIQ,13336
|
117
118
|
flwr/common/constant.py,sha256=FLqav6UCcdCG61XZr31fmAFqOu4WRFG8zcbnwUyYJ4w,6202
|
118
119
|
flwr/common/context.py,sha256=uJ-mnoC_8y_udEb3kAX-r8CPphNTWM72z1AlsvQEu54,2403
|
119
120
|
flwr/common/date.py,sha256=NHHpESce5wYqEwoDXf09gp9U9l_5Bmlh2BsOcwS-kDM,1554
|
@@ -225,7 +226,7 @@ flwr/server/compat/legacy_context.py,sha256=wBzBcfV6YO6IQGriM_FdJ5XZfiBBEEJdS_Od
|
|
225
226
|
flwr/server/criterion.py,sha256=ypbAexbztzGUxNen9RCHF91QeqiEQix4t4Ih3E-42MM,1061
|
226
227
|
flwr/server/driver/__init__.py,sha256=bikRv6CjTwSvYh7tf10gziU5o2YotOWhhftz2tr3KDc,886
|
227
228
|
flwr/server/driver/driver.py,sha256=u_fMfqLYTroTafGCNwKPHI4lttRL-Z5CqeT3_FHSq-Q,5701
|
228
|
-
flwr/server/driver/grpc_driver.py,sha256=
|
229
|
+
flwr/server/driver/grpc_driver.py,sha256=VYIbYavkJO2m6TcqStHr-pQLiyEoWlX8lsOsWfljZRQ,9758
|
229
230
|
flwr/server/driver/inmemory_driver.py,sha256=b1U1PrB_Vpn--vav8SX-sn0THD9BTlV9UFIpuWTWLro,6665
|
230
231
|
flwr/server/history.py,sha256=qSb5_pPTrwofpSYGsZWzMPkl_4uJ4mJFWesxXDrEvDU,5026
|
231
232
|
flwr/server/run_serverapp.py,sha256=vIPhvJx0i5sEZO4IKM6ruCXmx4ncat76rh0B4KhdhhM,2446
|
@@ -324,8 +325,8 @@ flwr/superexec/exec_servicer.py,sha256=X10ILT-AoGMrB3IgI2mBe9i-QcIVUAl9bucuqVOPY
|
|
324
325
|
flwr/superexec/exec_user_auth_interceptor.py,sha256=K06OU-l4LnYhTDg071hGJuOaQWEJbZsYi5qxUmmtiG0,3704
|
325
326
|
flwr/superexec/executor.py,sha256=_B55WW2TD1fBINpabSSDRenVHXYmvlfhv-k8hJKU4lQ,3115
|
326
327
|
flwr/superexec/simulation.py,sha256=WQDon15oqpMopAZnwRZoTICYCfHqtkvFSqiTQ2hLD_g,4088
|
327
|
-
flwr_nightly-1.15.0.
|
328
|
-
flwr_nightly-1.15.0.
|
329
|
-
flwr_nightly-1.15.0.
|
330
|
-
flwr_nightly-1.15.0.
|
331
|
-
flwr_nightly-1.15.0.
|
328
|
+
flwr_nightly-1.15.0.dev20250124.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
329
|
+
flwr_nightly-1.15.0.dev20250124.dist-info/METADATA,sha256=tr-JyBTsXNIUah1qY_BnD7Uhtsukv8Ktq5edbetabJs,15864
|
330
|
+
flwr_nightly-1.15.0.dev20250124.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
331
|
+
flwr_nightly-1.15.0.dev20250124.dist-info/entry_points.txt,sha256=JlNxX3qhaV18_2yj5a3kJW1ESxm31cal9iS_N_pf1Rk,538
|
332
|
+
flwr_nightly-1.15.0.dev20250124.dist-info/RECORD,,
|
{flwr_nightly-1.15.0.dev20250122.dist-info → flwr_nightly-1.15.0.dev20250124.dist-info}/LICENSE
RENAMED
File without changes
|
{flwr_nightly-1.15.0.dev20250122.dist-info → flwr_nightly-1.15.0.dev20250124.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|