flwr-nightly 1.15.0.dev20250129__py3-none-any.whl → 1.15.2.dev20250214__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/auth_plugin/oidc_cli_plugin.py +2 -2
- flwr/cli/ls.py +3 -2
- flwr/cli/new/templates/app/README.baseline.md.tpl +4 -4
- flwr/cli/new/templates/app/README.md.tpl +3 -2
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +3 -3
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +2 -2
- flwr/cli/stop.py +3 -2
- flwr/cli/utils.py +3 -3
- flwr/client/supernode/app.py +4 -28
- flwr/common/args.py +25 -47
- flwr/common/config.py +15 -2
- flwr/common/constant.py +9 -7
- flwr/common/exit_handlers.py +17 -29
- flwr/common/logger.py +10 -5
- flwr/common/object_ref.py +0 -14
- flwr/proto/clientappio_pb2.py +3 -13
- flwr/proto/clientappio_pb2_grpc.py +12 -63
- flwr/proto/error_pb2.py +3 -13
- flwr/proto/error_pb2_grpc.py +0 -20
- flwr/proto/exec_pb2.py +5 -15
- flwr/proto/exec_pb2_grpc.py +24 -105
- flwr/proto/fab_pb2.py +3 -13
- flwr/proto/fab_pb2_grpc.py +0 -20
- flwr/proto/fleet_pb2.py +4 -14
- flwr/proto/fleet_pb2_grpc.py +28 -119
- flwr/proto/grpcadapter_pb2.py +4 -14
- flwr/proto/grpcadapter_pb2_grpc.py +4 -35
- flwr/proto/log_pb2.py +3 -13
- flwr/proto/log_pb2_grpc.py +0 -20
- flwr/proto/message_pb2.py +5 -15
- flwr/proto/message_pb2_grpc.py +0 -20
- flwr/proto/node_pb2.py +3 -13
- flwr/proto/node_pb2_grpc.py +0 -20
- flwr/proto/recordset_pb2.py +8 -18
- flwr/proto/recordset_pb2_grpc.py +0 -20
- flwr/proto/run_pb2.py +6 -16
- flwr/proto/run_pb2_grpc.py +0 -20
- flwr/proto/serverappio_pb2.py +26 -45
- flwr/proto/serverappio_pb2.pyi +0 -62
- flwr/proto/serverappio_pb2_grpc.py +47 -264
- flwr/proto/serverappio_pb2_grpc.pyi +7 -27
- flwr/proto/simulationio_pb2.py +3 -13
- flwr/proto/simulationio_pb2_grpc.py +24 -105
- flwr/proto/task_pb2.py +9 -19
- flwr/proto/task_pb2.pyi +1 -4
- flwr/proto/task_pb2_grpc.py +0 -20
- flwr/proto/transport_pb2.py +10 -20
- flwr/proto/transport_pb2_grpc.py +4 -35
- flwr/server/app.py +5 -9
- flwr/server/driver/inmemory_driver.py +0 -1
- flwr/server/superlink/driver/serverappio_servicer.py +1 -99
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +17 -4
- flwr/server/superlink/fleet/message_handler/message_handler.py +0 -4
- flwr/server/superlink/fleet/vce/vce_api.py +0 -1
- flwr/server/superlink/linkstate/sqlite_linkstate.py +0 -6
- flwr/server/utils/validator.py +0 -3
- {flwr_nightly-1.15.0.dev20250129.dist-info → flwr_nightly-1.15.2.dev20250214.dist-info}/METADATA +4 -4
- {flwr_nightly-1.15.0.dev20250129.dist-info → flwr_nightly-1.15.2.dev20250214.dist-info}/RECORD +68 -68
- {flwr_nightly-1.15.0.dev20250129.dist-info → flwr_nightly-1.15.2.dev20250214.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.15.0.dev20250129.dist-info → flwr_nightly-1.15.2.dev20250214.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.15.0.dev20250129.dist-info → flwr_nightly-1.15.2.dev20250214.dist-info}/entry_points.txt +0 -0
@@ -26,7 +26,7 @@ import typer
|
|
26
26
|
from flwr.common.auth_plugin import CliAuthPlugin
|
27
27
|
from flwr.common.constant import (
|
28
28
|
ACCESS_TOKEN_KEY,
|
29
|
-
|
29
|
+
AUTH_TYPE_JSON_KEY,
|
30
30
|
REFRESH_TOKEN_KEY,
|
31
31
|
AuthType,
|
32
32
|
)
|
@@ -97,7 +97,7 @@ class OidcCliPlugin(CliAuthPlugin):
|
|
97
97
|
self.access_token = credentials.access_token
|
98
98
|
self.refresh_token = credentials.refresh_token
|
99
99
|
json_dict = {
|
100
|
-
|
100
|
+
AUTH_TYPE_JSON_KEY: AuthType.OIDC,
|
101
101
|
ACCESS_TOKEN_KEY: credentials.access_token,
|
102
102
|
REFRESH_TOKEN_KEY: credentials.refresh_token,
|
103
103
|
}
|
flwr/cli/ls.py
CHANGED
@@ -117,7 +117,7 @@ def ls( # pylint: disable=too-many-locals, too-many-branches, R0913, R0917
|
|
117
117
|
federation, config, federation_config_overrides
|
118
118
|
)
|
119
119
|
exit_if_no_address(federation_config, "ls")
|
120
|
-
|
120
|
+
channel = None
|
121
121
|
try:
|
122
122
|
if runs and run_id is not None:
|
123
123
|
raise ValueError(
|
@@ -148,7 +148,8 @@ def ls( # pylint: disable=too-many-locals, too-many-branches, R0913, R0917
|
|
148
148
|
)
|
149
149
|
raise typer.Exit(code=1) from err
|
150
150
|
finally:
|
151
|
-
channel
|
151
|
+
if channel:
|
152
|
+
channel.close()
|
152
153
|
except (typer.Exit, Exception) as err: # pylint: disable=broad-except
|
153
154
|
if suppress_output:
|
154
155
|
restore_output()
|
@@ -13,7 +13,7 @@ dataset: [dataset1, dataset2] # TODO: list of datasets you include in your basel
|
|
13
13
|
|
14
14
|
> [!IMPORTANT]
|
15
15
|
> To help having all baselines similarly formatted and structured, we have included two scripts in `baselines/dev` that when run will format your code and run some tests checking if it's formatted.
|
16
|
-
> These checks use standard packages such as `isort`, `black`, `pylint` and others. You as a baseline creator will need to install additional
|
16
|
+
> These checks use standard packages such as `isort`, `black`, `pylint` and others. You as a baseline creator will need to install additional packages. These are already specified in the `pyproject.toml` of
|
17
17
|
> your baseline. Follow these steps:
|
18
18
|
|
19
19
|
```bash
|
@@ -66,9 +66,9 @@ cd .. # so you are in the `flower/baselines` directory
|
|
66
66
|
|
67
67
|
## About this baseline
|
68
68
|
|
69
|
-
**What’s implemented:** :warning: *_Concisely describe what experiment(s) (e.g. Figure 1, Table 2, etc) in the publication can be replicated by running the code. Please only use a few sentences. ”_*
|
69
|
+
**What’s implemented:** :warning: *_Concisely describe what experiment(s) (e.g. Figure 1, Table 2, etc.) in the publication can be replicated by running the code. Please only use a few sentences. ”_*
|
70
70
|
|
71
|
-
**Datasets:** :warning: *_List the datasets you used (if you used a medium to large dataset, >10GB please also include the sizes of the dataset). We highly recommend using [FlowerDatasets](https://flower.ai/docs/datasets/index.html) to download and partition your dataset. If you have other ways to download the data, you can also use `FlowerDatasets` to
|
71
|
+
**Datasets:** :warning: *_List the datasets you used (if you used a medium to large dataset, >10GB please also include the sizes of the dataset). We highly recommend using [FlowerDatasets](https://flower.ai/docs/datasets/index.html) to download and partition your dataset. If you have other ways to download the data, you can also use `FlowerDatasets` to partition it._*
|
72
72
|
|
73
73
|
**Hardware Setup:** :warning: *_Give some details about the hardware (e.g. a server with 8x V100 32GB and 256GB of RAM) you used to run the experiments for this baseline. Indicate how long it took to run the experiments. Someone out there might not have access to the same resources you have so, could you list the absolute minimum hardware needed to run the experiment in a reasonable amount of time ? (e.g. minimum is 1x 16GB GPU otherwise a client model can’t be trained with a sufficiently large batch size). Could you test this works too?_*
|
74
74
|
|
@@ -122,6 +122,6 @@ flwr run . --run-config learning-rate=0.1,coefficient=0.123
|
|
122
122
|
flwr run . --run-config <my-big-experiment-config>.toml
|
123
123
|
```
|
124
124
|
|
125
|
-
:warning: _It is preferable to show a single
|
125
|
+
:warning: _It is preferable to show a single command (or multiple commands if they belong to the same experiment) and then a table/plot with the expected results, instead of showing all the commands first and then all the results/plots._
|
126
126
|
:warning: _If you present plots or other figures, please include either a Jupyter notebook showing how to create them or include a utility function that can be called after the experiments finish running._
|
127
127
|
:warning: If you include plots or figures, save them in `.png` format and place them in a new directory named `_static` at the same level as your `README.md`.
|
@@ -18,8 +18,9 @@ Refer to the [How to Run Simulations](https://flower.ai/docs/framework/how-to-ru
|
|
18
18
|
|
19
19
|
## Run with the Deployment Engine
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
Follow this [how-to guide](https://flower.ai/docs/framework/how-to-run-flower-with-deployment-engine.html) to run the same app in this example but with Flower's Deployment Engine. After that, you might be interested in setting up [secure TLS-enabled communications](https://flower.ai/docs/framework/how-to-enable-tls-connections.html) and [SuperNode authentication](https://flower.ai/docs/framework/how-to-authenticate-supernodes.html) in your federation.
|
22
|
+
|
23
|
+
You can run Flower on Docker too! Check out the [Flower with Docker](https://flower.ai/docs/framework/docker/index.html) documentation.
|
23
24
|
|
24
25
|
## Resources
|
25
26
|
|
@@ -8,10 +8,10 @@ version = "1.0.0"
|
|
8
8
|
description = ""
|
9
9
|
license = "Apache-2.0"
|
10
10
|
dependencies = [
|
11
|
-
"flwr[simulation]>=1.
|
12
|
-
"flwr-datasets[vision]>=0.
|
13
|
-
"torch==2.
|
14
|
-
"torchvision==0.
|
11
|
+
"flwr[simulation]>=1.15.2",
|
12
|
+
"flwr-datasets[vision]>=0.5.0",
|
13
|
+
"torch==2.5.1",
|
14
|
+
"torchvision==0.20.1",
|
15
15
|
]
|
16
16
|
|
17
17
|
[tool.hatch.metadata]
|
@@ -8,8 +8,8 @@ version = "1.0.0"
|
|
8
8
|
description = ""
|
9
9
|
license = "Apache-2.0"
|
10
10
|
dependencies = [
|
11
|
-
"flwr[simulation]>=1.
|
12
|
-
"flwr-datasets>=0.
|
11
|
+
"flwr[simulation]>=1.15.2",
|
12
|
+
"flwr-datasets>=0.5.0",
|
13
13
|
"torch==2.3.1",
|
14
14
|
"trl==0.8.1",
|
15
15
|
"bitsandbytes==0.45.0",
|
@@ -8,13 +8,13 @@ version = "1.0.0"
|
|
8
8
|
description = ""
|
9
9
|
license = "Apache-2.0"
|
10
10
|
dependencies = [
|
11
|
-
"flwr[simulation]>=1.
|
12
|
-
"flwr-datasets>=0.
|
13
|
-
"torch==2.
|
11
|
+
"flwr[simulation]>=1.15.2",
|
12
|
+
"flwr-datasets>=0.5.0",
|
13
|
+
"torch==2.5.1",
|
14
14
|
"transformers>=4.30.0,<5.0",
|
15
15
|
"evaluate>=0.4.0,<1.0",
|
16
16
|
"datasets>=2.0.0, <3.0",
|
17
|
-
"scikit-learn>=1.
|
17
|
+
"scikit-learn>=1.6.1, <2.0",
|
18
18
|
]
|
19
19
|
|
20
20
|
[tool.hatch.build.targets.wheel]
|
@@ -8,10 +8,10 @@ version = "1.0.0"
|
|
8
8
|
description = ""
|
9
9
|
license = "Apache-2.0"
|
10
10
|
dependencies = [
|
11
|
-
"flwr[simulation]>=1.
|
11
|
+
"flwr[simulation]>=1.15.2",
|
12
12
|
"jax==0.4.30",
|
13
13
|
"jaxlib==0.4.30",
|
14
|
-
"scikit-learn==1.
|
14
|
+
"scikit-learn==1.6.1",
|
15
15
|
]
|
16
16
|
|
17
17
|
[tool.hatch.build.targets.wheel]
|
@@ -8,8 +8,8 @@ version = "1.0.0"
|
|
8
8
|
description = ""
|
9
9
|
license = "Apache-2.0"
|
10
10
|
dependencies = [
|
11
|
-
"flwr[simulation]>=1.
|
12
|
-
"flwr-datasets[vision]>=0.
|
11
|
+
"flwr[simulation]>=1.15.2",
|
12
|
+
"flwr-datasets[vision]>=0.5.0",
|
13
13
|
"torch==2.5.1",
|
14
14
|
"torchvision==0.20.1",
|
15
15
|
]
|
@@ -8,9 +8,9 @@ version = "1.0.0"
|
|
8
8
|
description = ""
|
9
9
|
license = "Apache-2.0"
|
10
10
|
dependencies = [
|
11
|
-
"flwr[simulation]>=1.
|
12
|
-
"flwr-datasets[vision]>=0.
|
13
|
-
"scikit-learn>=1.
|
11
|
+
"flwr[simulation]>=1.15.2",
|
12
|
+
"flwr-datasets[vision]>=0.5.0",
|
13
|
+
"scikit-learn>=1.6.1",
|
14
14
|
]
|
15
15
|
|
16
16
|
[tool.hatch.build.targets.wheel]
|
flwr/cli/stop.py
CHANGED
@@ -84,7 +84,7 @@ def stop( # pylint: disable=R0914
|
|
84
84
|
federation, config, federation_config_overrides
|
85
85
|
)
|
86
86
|
exit_if_no_address(federation_config, "stop")
|
87
|
-
|
87
|
+
channel = None
|
88
88
|
try:
|
89
89
|
auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
|
90
90
|
channel = init_channel(app, federation_config, auth_plugin)
|
@@ -101,7 +101,8 @@ def stop( # pylint: disable=R0914
|
|
101
101
|
)
|
102
102
|
raise typer.Exit(code=1) from err
|
103
103
|
finally:
|
104
|
-
channel
|
104
|
+
if channel:
|
105
|
+
channel.close()
|
105
106
|
except (typer.Exit, Exception) as err: # pylint: disable=broad-except
|
106
107
|
if suppress_output:
|
107
108
|
restore_output()
|
flwr/cli/utils.py
CHANGED
@@ -28,7 +28,7 @@ import typer
|
|
28
28
|
|
29
29
|
from flwr.cli.cli_user_auth_interceptor import CliUserAuthInterceptor
|
30
30
|
from flwr.common.auth_plugin import CliAuthPlugin
|
31
|
-
from flwr.common.constant import
|
31
|
+
from flwr.common.constant import AUTH_TYPE_JSON_KEY, CREDENTIALS_DIR, FLWR_DIR
|
32
32
|
from flwr.common.grpc import (
|
33
33
|
GRPC_MAX_MESSAGE_LENGTH,
|
34
34
|
create_channel,
|
@@ -239,7 +239,7 @@ def try_obtain_cli_auth_plugin(
|
|
239
239
|
try:
|
240
240
|
with config_path.open("r", encoding="utf-8") as file:
|
241
241
|
json_file = json.load(file)
|
242
|
-
auth_type = json_file[
|
242
|
+
auth_type = json_file[AUTH_TYPE_JSON_KEY]
|
243
243
|
except (FileNotFoundError, KeyError):
|
244
244
|
typer.secho(
|
245
245
|
"❌ Missing or invalid credentials for user authentication. "
|
@@ -274,7 +274,7 @@ def init_channel(
|
|
274
274
|
interceptors: list[grpc.UnaryUnaryClientInterceptor] = []
|
275
275
|
if auth_plugin is not None:
|
276
276
|
auth_plugin.load_tokens()
|
277
|
-
interceptors
|
277
|
+
interceptors.append(CliUserAuthInterceptor(auth_plugin))
|
278
278
|
|
279
279
|
# Create the gRPC channel
|
280
280
|
channel = create_channel(
|
flwr/client/supernode/app.py
CHANGED
@@ -41,7 +41,7 @@ from flwr.common.constant import (
|
|
41
41
|
)
|
42
42
|
from flwr.common.exit import ExitCode, flwr_exit
|
43
43
|
from flwr.common.exit_handlers import register_exit_handlers
|
44
|
-
from flwr.common.logger import log
|
44
|
+
from flwr.common.logger import log
|
45
45
|
|
46
46
|
from ..app import start_client_internal
|
47
47
|
from ..clientapp.utils import get_load_client_app_fn
|
@@ -50,7 +50,6 @@ from ..clientapp.utils import get_load_client_app_fn
|
|
50
50
|
def run_supernode() -> None:
|
51
51
|
"""Run Flower SuperNode."""
|
52
52
|
args = _parse_args_run_supernode().parse_args()
|
53
|
-
_warn_deprecated_server_arg(args)
|
54
53
|
|
55
54
|
log(INFO, "Starting Flower SuperNode")
|
56
55
|
|
@@ -109,26 +108,6 @@ def run_client_app() -> None:
|
|
109
108
|
register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
|
110
109
|
|
111
110
|
|
112
|
-
def _warn_deprecated_server_arg(args: argparse.Namespace) -> None:
|
113
|
-
"""Warn about the deprecated argument `--server`."""
|
114
|
-
if args.server != FLEET_API_GRPC_RERE_DEFAULT_ADDRESS:
|
115
|
-
warn = "Passing flag --server is deprecated. Use --superlink instead."
|
116
|
-
warn_deprecated_feature(warn)
|
117
|
-
|
118
|
-
if args.superlink != FLEET_API_GRPC_RERE_DEFAULT_ADDRESS:
|
119
|
-
# if `--superlink` also passed, then
|
120
|
-
# warn user that this argument overrides what was passed with `--server`
|
121
|
-
log(
|
122
|
-
WARN,
|
123
|
-
"Both `--server` and `--superlink` were passed. "
|
124
|
-
"`--server` will be ignored. Connecting to the Superlink Fleet API "
|
125
|
-
"at %s.",
|
126
|
-
args.superlink,
|
127
|
-
)
|
128
|
-
else:
|
129
|
-
args.superlink = args.server
|
130
|
-
|
131
|
-
|
132
111
|
def _parse_args_run_supernode() -> argparse.ArgumentParser:
|
133
112
|
"""Parse flower-supernode command line arguments."""
|
134
113
|
parser = argparse.ArgumentParser(
|
@@ -206,15 +185,12 @@ def _parse_args_common(parser: argparse.ArgumentParser) -> None:
|
|
206
185
|
help="Specifies the path to the PEM-encoded root certificate file for "
|
207
186
|
"establishing secure HTTPS connections.",
|
208
187
|
)
|
209
|
-
parser.add_argument(
|
210
|
-
"--server",
|
211
|
-
default=FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
|
212
|
-
help="Server address",
|
213
|
-
)
|
214
188
|
parser.add_argument(
|
215
189
|
"--superlink",
|
216
190
|
default=FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
|
217
|
-
help="SuperLink Fleet API
|
191
|
+
help="SuperLink Fleet API address (IPv4, IPv6, or a domain name). If using the "
|
192
|
+
"REST (experimental) transport, ensure your address is in the form "
|
193
|
+
"`http://...` or `https://...` when TLS is enabled.",
|
218
194
|
)
|
219
195
|
parser.add_argument(
|
220
196
|
"--max-retries",
|
flwr/common/args.py
CHANGED
@@ -20,13 +20,9 @@ import sys
|
|
20
20
|
from logging import DEBUG, ERROR, WARN
|
21
21
|
from os.path import isfile
|
22
22
|
from pathlib import Path
|
23
|
-
from typing import Optional
|
23
|
+
from typing import Optional, Union
|
24
24
|
|
25
|
-
from flwr.common.constant import
|
26
|
-
TRANSPORT_TYPE_GRPC_ADAPTER,
|
27
|
-
TRANSPORT_TYPE_GRPC_RERE,
|
28
|
-
TRANSPORT_TYPE_REST,
|
29
|
-
)
|
25
|
+
from flwr.common.constant import TRANSPORT_TYPE_REST
|
30
26
|
from flwr.common.logger import log
|
31
27
|
|
32
28
|
|
@@ -55,9 +51,9 @@ def add_args_flwr_app_common(parser: argparse.ArgumentParser) -> None:
|
|
55
51
|
def try_obtain_root_certificates(
|
56
52
|
args: argparse.Namespace,
|
57
53
|
grpc_server_address: str,
|
58
|
-
) -> Optional[bytes]:
|
54
|
+
) -> Optional[Union[bytes, str]]:
|
59
55
|
"""Validate and return the root certificates."""
|
60
|
-
root_cert_path = args.root_certificates
|
56
|
+
root_cert_path: Optional[str] = args.root_certificates
|
61
57
|
if args.insecure:
|
62
58
|
if root_cert_path is not None:
|
63
59
|
sys.exit(
|
@@ -93,56 +89,38 @@ def try_obtain_root_certificates(
|
|
93
89
|
grpc_server_address,
|
94
90
|
root_cert_path,
|
95
91
|
)
|
92
|
+
if args.transport == TRANSPORT_TYPE_REST:
|
93
|
+
return root_cert_path
|
96
94
|
return root_certificates
|
97
95
|
|
98
96
|
|
99
97
|
def try_obtain_server_certificates(
|
100
98
|
args: argparse.Namespace,
|
101
|
-
transport_type: str,
|
102
99
|
) -> Optional[tuple[bytes, bytes, bytes]]:
|
103
100
|
"""Validate and return the CA cert, server cert, and server private key."""
|
104
101
|
if args.insecure:
|
105
102
|
log(WARN, "Option `--insecure` was set. Starting insecure HTTP server.")
|
106
103
|
return None
|
107
104
|
# Check if certificates are provided
|
108
|
-
if
|
109
|
-
if
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
)
|
128
|
-
if transport_type == TRANSPORT_TYPE_REST:
|
129
|
-
if args.ssl_certfile and args.ssl_keyfile:
|
130
|
-
if not isfile(args.ssl_certfile):
|
131
|
-
sys.exit("Path argument `--ssl-certfile` does not point to a file.")
|
132
|
-
if not isfile(args.ssl_keyfile):
|
133
|
-
sys.exit("Path argument `--ssl-keyfile` does not point to a file.")
|
134
|
-
certificates = (
|
135
|
-
b"",
|
136
|
-
Path(args.ssl_certfile).read_bytes(), # server certificate
|
137
|
-
Path(args.ssl_keyfile).read_bytes(), # server private key
|
138
|
-
)
|
139
|
-
return certificates
|
140
|
-
if args.ssl_certfile or args.ssl_keyfile:
|
141
|
-
sys.exit(
|
142
|
-
"You need to provide valid file paths to `--ssl-certfile` "
|
143
|
-
"and `--ssl-keyfile` to create a secure connection "
|
144
|
-
"in Fleet API server (REST, experimental)."
|
145
|
-
)
|
105
|
+
if args.ssl_certfile and args.ssl_keyfile and args.ssl_ca_certfile:
|
106
|
+
if not isfile(args.ssl_ca_certfile):
|
107
|
+
sys.exit("Path argument `--ssl-ca-certfile` does not point to a file.")
|
108
|
+
if not isfile(args.ssl_certfile):
|
109
|
+
sys.exit("Path argument `--ssl-certfile` does not point to a file.")
|
110
|
+
if not isfile(args.ssl_keyfile):
|
111
|
+
sys.exit("Path argument `--ssl-keyfile` does not point to a file.")
|
112
|
+
certificates = (
|
113
|
+
Path(args.ssl_ca_certfile).read_bytes(), # CA certificate
|
114
|
+
Path(args.ssl_certfile).read_bytes(), # server certificate
|
115
|
+
Path(args.ssl_keyfile).read_bytes(), # server private key
|
116
|
+
)
|
117
|
+
return certificates
|
118
|
+
if args.ssl_certfile or args.ssl_keyfile or args.ssl_ca_certfile:
|
119
|
+
sys.exit(
|
120
|
+
"You need to provide valid file paths to `--ssl-certfile`, "
|
121
|
+
"`--ssl-keyfile`, and `—-ssl-ca-certfile` to create a secure "
|
122
|
+
"connection in Fleet API server (gRPC-rere)."
|
123
|
+
)
|
146
124
|
log(
|
147
125
|
ERROR,
|
148
126
|
"Certificates are required unless running in insecure mode. "
|
flwr/common/config.py
CHANGED
@@ -23,6 +23,7 @@ from pathlib import Path
|
|
23
23
|
from typing import IO, Any, Optional, TypeVar, Union, cast, get_args
|
24
24
|
|
25
25
|
import tomli
|
26
|
+
import typer
|
26
27
|
|
27
28
|
from flwr.common.constant import (
|
28
29
|
APP_DIR,
|
@@ -233,8 +234,20 @@ def parse_config_args(
|
|
233
234
|
|
234
235
|
matches = pattern.findall(config_line)
|
235
236
|
toml_str = "\n".join(f"{k} = {v}" for k, v in matches)
|
236
|
-
|
237
|
-
|
237
|
+
try:
|
238
|
+
overrides.update(tomli.loads(toml_str))
|
239
|
+
flat_overrides = flatten_dict(overrides) if flatten else overrides
|
240
|
+
except tomli.TOMLDecodeError as err:
|
241
|
+
typer.secho(
|
242
|
+
"❌ The provided configuration string is in an invalid format. "
|
243
|
+
"The correct format should be, e.g., 'key1=123 key2=false "
|
244
|
+
'key3="string"\', where values must be of type bool, int, '
|
245
|
+
"string, or float. Ensure proper formatting with "
|
246
|
+
"space-separated key-value pairs.",
|
247
|
+
fg=typer.colors.RED,
|
248
|
+
bold=True,
|
249
|
+
)
|
250
|
+
raise typer.Exit(code=1) from err
|
238
251
|
|
239
252
|
return flat_overrides
|
240
253
|
|
flwr/common/constant.py
CHANGED
@@ -108,15 +108,17 @@ MAX_RETRY_DELAY = 20 # Maximum delay duration between two consecutive retries.
|
|
108
108
|
|
109
109
|
# Constants for user authentication
|
110
110
|
CREDENTIALS_DIR = ".credentials"
|
111
|
-
|
112
|
-
|
113
|
-
|
111
|
+
AUTH_TYPE_JSON_KEY = "auth-type" # For key name in JSON file
|
112
|
+
AUTH_TYPE_YAML_KEY = "auth_type" # For key name in YAML file
|
113
|
+
ACCESS_TOKEN_KEY = "flwr-oidc-access-token"
|
114
|
+
REFRESH_TOKEN_KEY = "flwr-oidc-refresh-token"
|
114
115
|
|
115
116
|
# Constants for node authentication
|
116
|
-
PUBLIC_KEY_HEADER = "public-key-bin" # Must end with "-bin" for binary data
|
117
|
-
SIGNATURE_HEADER = "signature-bin" # Must end with "-bin" for binary data
|
118
|
-
TIMESTAMP_HEADER = "timestamp"
|
119
|
-
TIMESTAMP_TOLERANCE = 10 #
|
117
|
+
PUBLIC_KEY_HEADER = "flwr-public-key-bin" # Must end with "-bin" for binary data
|
118
|
+
SIGNATURE_HEADER = "flwr-signature-bin" # Must end with "-bin" for binary data
|
119
|
+
TIMESTAMP_HEADER = "flwr-timestamp"
|
120
|
+
TIMESTAMP_TOLERANCE = 10 # General tolerance for timestamp verification
|
121
|
+
SYSTEM_TIME_TOLERANCE = 5 # Allowance for system time drift
|
120
122
|
|
121
123
|
|
122
124
|
class MessageType:
|
flwr/common/exit_handlers.py
CHANGED
@@ -15,10 +15,10 @@
|
|
15
15
|
"""Common function to register exit handlers for server and client."""
|
16
16
|
|
17
17
|
|
18
|
-
|
18
|
+
import signal
|
19
19
|
from threading import Thread
|
20
20
|
from types import FrameType
|
21
|
-
from typing import Optional
|
21
|
+
from typing import Callable, Optional
|
22
22
|
|
23
23
|
from grpc import Server
|
24
24
|
|
@@ -26,12 +26,15 @@ from flwr.common.telemetry import EventType
|
|
26
26
|
|
27
27
|
from .exit import ExitCode, flwr_exit
|
28
28
|
|
29
|
-
SIGNAL_TO_EXIT_CODE = {
|
30
|
-
SIGINT: ExitCode.GRACEFUL_EXIT_SIGINT,
|
31
|
-
|
32
|
-
SIGTERM: ExitCode.GRACEFUL_EXIT_SIGTERM,
|
29
|
+
SIGNAL_TO_EXIT_CODE: dict[int, int] = {
|
30
|
+
signal.SIGINT: ExitCode.GRACEFUL_EXIT_SIGINT,
|
31
|
+
signal.SIGTERM: ExitCode.GRACEFUL_EXIT_SIGTERM,
|
33
32
|
}
|
34
33
|
|
34
|
+
# SIGQUIT is not available on Windows
|
35
|
+
if hasattr(signal, "SIGQUIT"):
|
36
|
+
SIGNAL_TO_EXIT_CODE[signal.SIGQUIT] = ExitCode.GRACEFUL_EXIT_SIGQUIT
|
37
|
+
|
35
38
|
|
36
39
|
def register_exit_handlers(
|
37
40
|
event_type: EventType,
|
@@ -54,23 +57,16 @@ def register_exit_handlers(
|
|
54
57
|
An optional list of threads that need to be gracefully
|
55
58
|
terminated before exiting.
|
56
59
|
"""
|
57
|
-
default_handlers = {
|
58
|
-
|
59
|
-
|
60
|
-
SIGTERM: None,
|
61
|
-
}
|
62
|
-
|
63
|
-
def graceful_exit_handler( # type: ignore
|
64
|
-
signalnum,
|
65
|
-
frame: FrameType, # pylint: disable=unused-argument
|
66
|
-
) -> None:
|
60
|
+
default_handlers: dict[int, Callable[[int, FrameType], None]] = {}
|
61
|
+
|
62
|
+
def graceful_exit_handler(signalnum: int, _frame: FrameType) -> None:
|
67
63
|
"""Exit handler to be registered with `signal.signal`.
|
68
64
|
|
69
65
|
When called will reset signal handler to original signal handler from
|
70
66
|
default_handlers.
|
71
67
|
"""
|
72
68
|
# Reset to default handler
|
73
|
-
signal(signalnum, default_handlers[signalnum])
|
69
|
+
signal.signal(signalnum, default_handlers[signalnum]) # type: ignore
|
74
70
|
|
75
71
|
if grpc_servers is not None:
|
76
72
|
for grpc_server in grpc_servers:
|
@@ -87,15 +83,7 @@ def register_exit_handlers(
|
|
87
83
|
event_type=event_type,
|
88
84
|
)
|
89
85
|
|
90
|
-
|
91
|
-
|
92
|
-
graceful_exit_handler
|
93
|
-
|
94
|
-
default_handlers[SIGQUIT] = signal( # type: ignore
|
95
|
-
SIGQUIT,
|
96
|
-
graceful_exit_handler, # type: ignore
|
97
|
-
)
|
98
|
-
default_handlers[SIGTERM] = signal( # type: ignore
|
99
|
-
SIGTERM,
|
100
|
-
graceful_exit_handler, # type: ignore
|
101
|
-
)
|
86
|
+
# Register signal handlers
|
87
|
+
for sig in SIGNAL_TO_EXIT_CODE:
|
88
|
+
default_handler = signal.signal(sig, graceful_exit_handler) # type: ignore
|
89
|
+
default_handlers[sig] = default_handler # type: ignore
|
flwr/common/logger.py
CHANGED
@@ -128,12 +128,17 @@ console_handler.setLevel(logging.INFO)
|
|
128
128
|
FLOWER_LOGGER.addHandler(console_handler)
|
129
129
|
|
130
130
|
# Set log level via env var (show timestamps for `DEBUG`)
|
131
|
-
if log_level := os.getenv("
|
131
|
+
if log_level := os.getenv("FLWR_LOG_LEVEL"):
|
132
|
+
log_level = log_level.upper()
|
132
133
|
try:
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
134
|
+
is_debug = log_level == "DEBUG"
|
135
|
+
if is_debug:
|
136
|
+
log(
|
137
|
+
WARN,
|
138
|
+
"DEBUG logs enabled. Do not use this in production, as it may expose "
|
139
|
+
"sensitive details.",
|
140
|
+
)
|
141
|
+
update_console_handler(level=log_level, timestamps=is_debug, colored=True)
|
137
142
|
except Exception: # pylint: disable=broad-exception-caught
|
138
143
|
# Alert user but don't raise exception
|
139
144
|
log(
|
flwr/common/object_ref.py
CHANGED
@@ -19,13 +19,10 @@ import ast
|
|
19
19
|
import importlib
|
20
20
|
import sys
|
21
21
|
from importlib.util import find_spec
|
22
|
-
from logging import WARN
|
23
22
|
from pathlib import Path
|
24
23
|
from threading import Lock
|
25
24
|
from typing import Any, Optional, Union
|
26
25
|
|
27
|
-
from .logger import log
|
28
|
-
|
29
26
|
OBJECT_REF_HELP_STR = """
|
30
27
|
\n\nThe object reference string should have the form <module>:<attribute>. Valid
|
31
28
|
examples include `client:app` and `project.package.module:wrapper.app`. It must
|
@@ -171,17 +168,6 @@ def load_app( # pylint: disable= too-many-branches
|
|
171
168
|
# Import the module
|
172
169
|
if module_str not in sys.modules:
|
173
170
|
module = importlib.import_module(module_str)
|
174
|
-
# Hack: `tabnet` does not work with `importlib.reload`
|
175
|
-
elif "tabnet" in sys.modules:
|
176
|
-
log(
|
177
|
-
WARN,
|
178
|
-
"Cannot reload module `%s` from disk due to compatibility issues "
|
179
|
-
"with the `tabnet` library. The module will be loaded from the "
|
180
|
-
"cache instead. If you experience issues, consider restarting "
|
181
|
-
"the application.",
|
182
|
-
module_str,
|
183
|
-
)
|
184
|
-
module = sys.modules[module_str]
|
185
171
|
else:
|
186
172
|
module = sys.modules[module_str]
|
187
173
|
_reload_modules(project_dir)
|
flwr/proto/clientappio_pb2.py
CHANGED
@@ -1,22 +1,12 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
3
|
-
# NO CHECKED-IN PROTOBUF GENCODE
|
4
3
|
# source: flwr/proto/clientappio.proto
|
5
|
-
# Protobuf Python Version:
|
4
|
+
# Protobuf Python Version: 4.25.1
|
6
5
|
"""Generated protocol buffer code."""
|
7
6
|
from google.protobuf import descriptor as _descriptor
|
8
7
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
9
|
-
from google.protobuf import runtime_version as _runtime_version
|
10
8
|
from google.protobuf import symbol_database as _symbol_database
|
11
9
|
from google.protobuf.internal import builder as _builder
|
12
|
-
_runtime_version.ValidateProtobufRuntimeVersion(
|
13
|
-
_runtime_version.Domain.PUBLIC,
|
14
|
-
5,
|
15
|
-
29,
|
16
|
-
0,
|
17
|
-
'',
|
18
|
-
'flwr/proto/clientappio.proto'
|
19
|
-
)
|
20
10
|
# @@protoc_insertion_point(imports)
|
21
11
|
|
22
12
|
_sym_db = _symbol_database.Default()
|
@@ -32,8 +22,8 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x66lwr/proto/
|
|
32
22
|
_globals = globals()
|
33
23
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
34
24
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'flwr.proto.clientappio_pb2', _globals)
|
35
|
-
if
|
36
|
-
DESCRIPTOR.
|
25
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
26
|
+
DESCRIPTOR._options = None
|
37
27
|
_globals['_CLIENTAPPOUTPUTCODE']._serialized_start=675
|
38
28
|
_globals['_CLIENTAPPOUTPUTCODE']._serialized_end=751
|
39
29
|
_globals['_CLIENTAPPOUTPUTSTATUS']._serialized_start=114
|