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.
Files changed (68) hide show
  1. flwr/cli/auth_plugin/oidc_cli_plugin.py +2 -2
  2. flwr/cli/ls.py +3 -2
  3. flwr/cli/new/templates/app/README.baseline.md.tpl +4 -4
  4. flwr/cli/new/templates/app/README.md.tpl +3 -2
  5. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
  6. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +2 -2
  7. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
  8. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +2 -2
  9. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +2 -2
  10. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +2 -2
  11. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +2 -2
  12. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +3 -3
  13. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +2 -2
  14. flwr/cli/stop.py +3 -2
  15. flwr/cli/utils.py +3 -3
  16. flwr/client/supernode/app.py +4 -28
  17. flwr/common/args.py +25 -47
  18. flwr/common/config.py +15 -2
  19. flwr/common/constant.py +9 -7
  20. flwr/common/exit_handlers.py +17 -29
  21. flwr/common/logger.py +10 -5
  22. flwr/common/object_ref.py +0 -14
  23. flwr/proto/clientappio_pb2.py +3 -13
  24. flwr/proto/clientappio_pb2_grpc.py +12 -63
  25. flwr/proto/error_pb2.py +3 -13
  26. flwr/proto/error_pb2_grpc.py +0 -20
  27. flwr/proto/exec_pb2.py +5 -15
  28. flwr/proto/exec_pb2_grpc.py +24 -105
  29. flwr/proto/fab_pb2.py +3 -13
  30. flwr/proto/fab_pb2_grpc.py +0 -20
  31. flwr/proto/fleet_pb2.py +4 -14
  32. flwr/proto/fleet_pb2_grpc.py +28 -119
  33. flwr/proto/grpcadapter_pb2.py +4 -14
  34. flwr/proto/grpcadapter_pb2_grpc.py +4 -35
  35. flwr/proto/log_pb2.py +3 -13
  36. flwr/proto/log_pb2_grpc.py +0 -20
  37. flwr/proto/message_pb2.py +5 -15
  38. flwr/proto/message_pb2_grpc.py +0 -20
  39. flwr/proto/node_pb2.py +3 -13
  40. flwr/proto/node_pb2_grpc.py +0 -20
  41. flwr/proto/recordset_pb2.py +8 -18
  42. flwr/proto/recordset_pb2_grpc.py +0 -20
  43. flwr/proto/run_pb2.py +6 -16
  44. flwr/proto/run_pb2_grpc.py +0 -20
  45. flwr/proto/serverappio_pb2.py +26 -45
  46. flwr/proto/serverappio_pb2.pyi +0 -62
  47. flwr/proto/serverappio_pb2_grpc.py +47 -264
  48. flwr/proto/serverappio_pb2_grpc.pyi +7 -27
  49. flwr/proto/simulationio_pb2.py +3 -13
  50. flwr/proto/simulationio_pb2_grpc.py +24 -105
  51. flwr/proto/task_pb2.py +9 -19
  52. flwr/proto/task_pb2.pyi +1 -4
  53. flwr/proto/task_pb2_grpc.py +0 -20
  54. flwr/proto/transport_pb2.py +10 -20
  55. flwr/proto/transport_pb2_grpc.py +4 -35
  56. flwr/server/app.py +5 -9
  57. flwr/server/driver/inmemory_driver.py +0 -1
  58. flwr/server/superlink/driver/serverappio_servicer.py +1 -99
  59. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +17 -4
  60. flwr/server/superlink/fleet/message_handler/message_handler.py +0 -4
  61. flwr/server/superlink/fleet/vce/vce_api.py +0 -1
  62. flwr/server/superlink/linkstate/sqlite_linkstate.py +0 -6
  63. flwr/server/utils/validator.py +0 -3
  64. {flwr_nightly-1.15.0.dev20250129.dist-info → flwr_nightly-1.15.2.dev20250214.dist-info}/METADATA +4 -4
  65. {flwr_nightly-1.15.0.dev20250129.dist-info → flwr_nightly-1.15.2.dev20250214.dist-info}/RECORD +68 -68
  66. {flwr_nightly-1.15.0.dev20250129.dist-info → flwr_nightly-1.15.2.dev20250214.dist-info}/LICENSE +0 -0
  67. {flwr_nightly-1.15.0.dev20250129.dist-info → flwr_nightly-1.15.2.dev20250214.dist-info}/WHEEL +0 -0
  68. {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
- AUTH_TYPE_KEY,
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
- AUTH_TYPE_KEY: AuthType.OIDC,
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.close()
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 pacakges. These are already specified in the `pyproject.toml` of
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 partiion it._*
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 commmand (or multilple 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._
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
- > \[!NOTE\]
22
- > An update to this example will show how to run this Flower application with the Deployment Engine and TLS certificates, or with Docker.
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.14.0",
12
- "flwr-datasets[vision]>=0.3.0",
13
- "torch==2.2.1",
14
- "torchvision==0.17.1",
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.14.0",
12
- "flwr-datasets>=0.3.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.14.0",
12
- "flwr-datasets>=0.3.0",
13
- "torch==2.2.1",
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.3.1, <2.0",
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.14.0",
11
+ "flwr[simulation]>=1.15.2",
12
12
  "jax==0.4.30",
13
13
  "jaxlib==0.4.30",
14
- "scikit-learn==1.3.2",
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.14.0",
12
- "flwr-datasets[vision]>=0.3.0",
11
+ "flwr[simulation]>=1.15.2",
12
+ "flwr-datasets[vision]>=0.5.0",
13
13
  "mlx==0.21.1",
14
14
  ]
15
15
 
@@ -8,8 +8,8 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.14.0",
12
- "numpy>=1.21.0",
11
+ "flwr[simulation]>=1.15.2",
12
+ "numpy>=2.0.2",
13
13
  ]
14
14
 
15
15
  [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.14.0",
12
- "flwr-datasets[vision]>=0.3.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.14.0",
12
- "flwr-datasets[vision]>=0.3.0",
13
- "scikit-learn>=1.1.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]
@@ -8,8 +8,8 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.14.0",
12
- "flwr-datasets[vision]>=0.3.0",
11
+ "flwr[simulation]>=1.15.2",
12
+ "flwr-datasets[vision]>=0.5.0",
13
13
  "tensorflow>=2.11.1,<2.18.0",
14
14
  ]
15
15
 
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.close()
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 AUTH_TYPE_KEY, CREDENTIALS_DIR, FLWR_DIR
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[AUTH_TYPE_KEY]
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 = CliUserAuthInterceptor(auth_plugin)
277
+ interceptors.append(CliUserAuthInterceptor(auth_plugin))
278
278
 
279
279
  # Create the gRPC channel
280
280
  channel = create_channel(
@@ -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, warn_deprecated_feature
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 (gRPC-rere) address (IPv4, IPv6, or a domain name)",
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 transport_type in [TRANSPORT_TYPE_GRPC_RERE, TRANSPORT_TYPE_GRPC_ADAPTER]:
109
- if args.ssl_certfile and args.ssl_keyfile and args.ssl_ca_certfile:
110
- if not isfile(args.ssl_ca_certfile):
111
- sys.exit("Path argument `--ssl-ca-certfile` does not point to a file.")
112
- if not isfile(args.ssl_certfile):
113
- sys.exit("Path argument `--ssl-certfile` does not point to a file.")
114
- if not isfile(args.ssl_keyfile):
115
- sys.exit("Path argument `--ssl-keyfile` does not point to a file.")
116
- certificates = (
117
- Path(args.ssl_ca_certfile).read_bytes(), # CA certificate
118
- Path(args.ssl_certfile).read_bytes(), # server certificate
119
- Path(args.ssl_keyfile).read_bytes(), # server private key
120
- )
121
- return certificates
122
- if args.ssl_certfile or args.ssl_keyfile or args.ssl_ca_certfile:
123
- sys.exit(
124
- "You need to provide valid file paths to `--ssl-certfile`, "
125
- "`--ssl-keyfile`, and `—-ssl-ca-certfile` to create a secure "
126
- "connection in Fleet API server (gRPC-rere)."
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
- overrides.update(tomli.loads(toml_str))
237
- flat_overrides = flatten_dict(overrides) if flatten else overrides
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
- AUTH_TYPE_KEY = "auth_type"
112
- ACCESS_TOKEN_KEY = "access_token"
113
- REFRESH_TOKEN_KEY = "refresh_token"
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 # Tolerance for timestamp verification
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:
@@ -15,10 +15,10 @@
15
15
  """Common function to register exit handlers for server and client."""
16
16
 
17
17
 
18
- from signal import SIGINT, SIGQUIT, SIGTERM, signal
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
- SIGQUIT: ExitCode.GRACEFUL_EXIT_SIGQUIT,
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
- SIGINT: None,
59
- SIGQUIT: None,
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
- default_handlers[SIGINT] = signal( # type: ignore
91
- SIGINT,
92
- graceful_exit_handler, # type: ignore
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("PYTHONLOGLEVEL"):
131
+ if log_level := os.getenv("FLWR_LOG_LEVEL"):
132
+ log_level = log_level.upper()
132
133
  try:
133
- use_time_stamps = log_level.upper() == "DEBUG"
134
- update_console_handler(
135
- level=log_level, timestamps=use_time_stamps, colored=True
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)
@@ -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: 5.29.0
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 not _descriptor._USE_C_DESCRIPTORS:
36
- DESCRIPTOR._loaded_options = None
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