flwr-nightly 1.13.0.dev20241117__py3-none-any.whl → 1.14.0.dev20241126__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.

Files changed (31) hide show
  1. flwr/cli/build.py +0 -37
  2. flwr/cli/install.py +1 -19
  3. flwr/cli/ls.py +1 -1
  4. flwr/cli/new/new.py +23 -13
  5. flwr/cli/new/templates/app/README.md.tpl +11 -0
  6. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
  7. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +1 -1
  8. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
  9. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
  10. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
  11. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
  12. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
  13. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
  14. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
  15. flwr/cli/run/run.py +4 -2
  16. flwr/client/app.py +4 -13
  17. flwr/client/clientapp/app.py +10 -4
  18. flwr/client/grpc_rere_client/connection.py +5 -0
  19. flwr/client/supernode/app.py +1 -27
  20. flwr/common/args.py +17 -13
  21. flwr/common/constant.py +3 -0
  22. flwr/common/grpc.py +4 -1
  23. flwr/server/app.py +9 -10
  24. flwr/server/driver/grpc_driver.py +63 -3
  25. flwr/server/serverapp/app.py +17 -8
  26. flwr/simulation/app.py +50 -54
  27. {flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/METADATA +1 -2
  28. {flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/RECORD +31 -31
  29. {flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/LICENSE +0 -0
  30. {flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/WHEEL +0 -0
  31. {flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/entry_points.txt +0 -0
flwr/cli/build.py CHANGED
@@ -19,18 +19,14 @@ import os
19
19
  import shutil
20
20
  import tempfile
21
21
  import zipfile
22
- from logging import DEBUG, ERROR
23
22
  from pathlib import Path
24
23
  from typing import Annotated, Any, Optional, Union
25
24
 
26
25
  import pathspec
27
26
  import tomli_w
28
27
  import typer
29
- from hatchling.builders.wheel import WheelBuilder
30
- from hatchling.metadata.core import ProjectMetadata
31
28
 
32
29
  from flwr.common.constant import FAB_ALLOWED_EXTENSIONS, FAB_DATE, FAB_HASH_TRUNCATION
33
- from flwr.common.logger import log
34
30
 
35
31
  from .config_utils import load_and_validate
36
32
  from .utils import is_valid_project_name
@@ -55,27 +51,6 @@ def get_fab_filename(conf: dict[str, Any], fab_hash: str) -> str:
55
51
  return f"{publisher}.{name}.{version}.{fab_hash_truncated}.fab"
56
52
 
57
53
 
58
- def _build_app_wheel(app: Path) -> Path:
59
- """Build app as a wheel and return its path."""
60
- # Path to your project directory
61
- app_dir = str(app.resolve())
62
- try:
63
-
64
- # Initialize the WheelBuilder
65
- builder = WheelBuilder(
66
- app_dir, metadata=ProjectMetadata(root=app_dir, plugin_manager=None)
67
- )
68
-
69
- # Build
70
- whl_path = Path(next(builder.build(directory=app_dir)))
71
- log(DEBUG, "Wheel succesfully built: %s", str(whl_path))
72
- except Exception as ex:
73
- log(ERROR, "Exception encountered when building wheel.", exc_info=ex)
74
- raise typer.Exit(code=1) from ex
75
-
76
- return whl_path
77
-
78
-
79
54
  # pylint: disable=too-many-locals, too-many-statements
80
55
  def build(
81
56
  app: Annotated[
@@ -131,12 +106,6 @@ def build(
131
106
  bold=True,
132
107
  )
133
108
 
134
- # Build wheel
135
- whl_path = _build_app_wheel(app)
136
-
137
- # Add path to .whl to `[tool.flwr.app]`
138
- conf["tool"]["flwr"]["app"]["whl"] = str(whl_path.name)
139
-
140
109
  # Load .gitignore rules if present
141
110
  ignore_spec = _load_gitignore(app)
142
111
 
@@ -168,9 +137,6 @@ def build(
168
137
  and f.name != "pyproject.toml" # Exclude the original pyproject.toml
169
138
  ]
170
139
 
171
- # Include FAB .whl
172
- all_files.append(whl_path)
173
-
174
140
  for file_path in all_files:
175
141
  # Read the file content manually
176
142
  with open(file_path, "rb") as f:
@@ -187,9 +153,6 @@ def build(
187
153
  # Add CONTENT and CONTENT.jwt to the zip file
188
154
  write_to_zip(fab_file, ".info/CONTENT", list_file_content)
189
155
 
190
- # Erase FAB .whl in app directory
191
- whl_path.unlink()
192
-
193
156
  # Get hash of FAB file
194
157
  content = Path(temp_filename).read_bytes()
195
158
  fab_hash = hashlib.sha256(content).hexdigest()
flwr/cli/install.py CHANGED
@@ -16,7 +16,6 @@
16
16
 
17
17
  import hashlib
18
18
  import shutil
19
- import subprocess
20
19
  import tempfile
21
20
  import zipfile
22
21
  from io import BytesIO
@@ -188,25 +187,8 @@ def validate_and_install(
188
187
  else:
189
188
  shutil.copy2(item, install_dir / item.name)
190
189
 
191
- whl_file = config["tool"]["flwr"]["app"]["whl"]
192
- install_whl = install_dir / whl_file
193
- try:
194
- subprocess.run(
195
- ["pip", "install", "--no-deps", install_whl],
196
- capture_output=True,
197
- text=True,
198
- check=True,
199
- )
200
- except subprocess.CalledProcessError as e:
201
- typer.secho(
202
- f"❌ Failed to install {project_name}:\n{e.stderr}",
203
- fg=typer.colors.RED,
204
- bold=True,
205
- )
206
- raise typer.Exit(code=1) from e
207
-
208
190
  typer.secho(
209
- f"🎊 Successfully installed {project_name}.",
191
+ f"🎊 Successfully installed {project_name} to {install_dir}.",
210
192
  fg=typer.colors.GREEN,
211
193
  bold=True,
212
194
  )
flwr/cli/ls.py CHANGED
@@ -82,7 +82,7 @@ def ls(
82
82
 
83
83
  if "address" not in federation_config:
84
84
  typer.secho(
85
- "❌ `flwr log` currently works with Exec API. Ensure that the correct"
85
+ "❌ `flwr ls` currently works with Exec API. Ensure that the correct"
86
86
  "Exec API address is provided in the `pyproject.toml`.",
87
87
  fg=typer.colors.RED,
88
88
  bold=True,
flwr/cli/new/new.py CHANGED
@@ -268,20 +268,30 @@ def new(
268
268
  context=context,
269
269
  )
270
270
 
271
- print(
272
- typer.style(
273
- "🎊 Flower App creation successful.\n\n"
274
- "Use the following command to run your Flower App:\n",
275
- fg=typer.colors.GREEN,
276
- bold=True,
277
- )
271
+ prompt = typer.style(
272
+ "🎊 Flower App creation successful.\n\n"
273
+ "To run your Flower App, use the following command:\n\n",
274
+ fg=typer.colors.GREEN,
275
+ bold=True,
278
276
  )
279
277
 
280
278
  _add = " huggingface-cli login\n" if llm_challenge_str else ""
281
- print(
282
- typer.style(
283
- f" cd {package_name}\n" + " pip install -e .\n" + _add + " flwr run\n",
284
- fg=typer.colors.BRIGHT_CYAN,
285
- bold=True,
286
- )
279
+ prompt += typer.style(
280
+ _add + f" flwr run {package_name}\n\n",
281
+ fg=typer.colors.BRIGHT_CYAN,
282
+ bold=True,
283
+ )
284
+
285
+ prompt += typer.style(
286
+ "If you haven't installed all dependencies yet, follow these steps:\n\n",
287
+ fg=typer.colors.GREEN,
288
+ bold=True,
287
289
  )
290
+
291
+ prompt += typer.style(
292
+ f" cd {package_name}\n" + " pip install -e .\n" + _add + " flwr run .\n",
293
+ fg=typer.colors.BRIGHT_CYAN,
294
+ bold=True,
295
+ )
296
+
297
+ print(prompt)
@@ -14,7 +14,18 @@ In the `$project_name` directory, use `flwr run` to run a local simulation:
14
14
  flwr run .
15
15
  ```
16
16
 
17
+ Refer to the [How to Run Simulations](https://flower.ai/docs/framework/how-to-run-simulations.html) guide in the documentation for advice on how to optimize your simulations.
18
+
17
19
  ## Run with the Deployment Engine
18
20
 
19
21
  > \[!NOTE\]
20
22
  > An update to this example will show how to run this Flower application with the Deployment Engine and TLS certificates, or with Docker.
23
+
24
+ ## Resources
25
+
26
+ - Flower website: [flower.ai](https://flower.ai/)
27
+ - Check the documentation: [flower.ai/docs](https://flower.ai/docs/)
28
+ - Give Flower a ⭐️ on GitHub: [GitHub](https://github.com/adap/flower)
29
+ - Join the Flower community!
30
+ - [Flower Slack](https://flower.ai/join-slack/)
31
+ - [Flower Discuss](https://discuss.flower.ai/)
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.0",
12
12
  "flwr-datasets[vision]>=0.3.0",
13
13
  "torch==2.2.1",
14
14
  "torchvision==0.17.1",
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.0",
12
12
  "flwr-datasets>=0.3.0",
13
13
  "torch==2.3.1",
14
14
  "trl==0.8.1",
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.0",
12
12
  "flwr-datasets>=0.3.0",
13
13
  "torch==2.2.1",
14
14
  "transformers>=4.30.0,<5.0",
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.0",
12
12
  "jax==0.4.30",
13
13
  "jaxlib==0.4.30",
14
14
  "scikit-learn==1.3.2",
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.0",
12
12
  "flwr-datasets[vision]>=0.3.0",
13
13
  "mlx==0.16.1",
14
14
  "numpy==1.24.4",
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.0",
12
12
  "numpy>=1.21.0",
13
13
  ]
14
14
 
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.0",
12
12
  "flwr-datasets[vision]>=0.3.0",
13
13
  "torch==2.2.1",
14
14
  "torchvision==0.17.1",
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.0",
12
12
  "flwr-datasets[vision]>=0.3.0",
13
13
  "scikit-learn>=1.1.1",
14
14
  ]
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.12.0",
11
+ "flwr[simulation]>=1.13.0",
12
12
  "flwr-datasets[vision]>=0.3.0",
13
13
  "tensorflow>=2.11.1,<2.18.0",
14
14
  ]
flwr/cli/run/run.py CHANGED
@@ -125,6 +125,10 @@ def _run_with_exec_api(
125
125
 
126
126
  fab_path, fab_hash = build(app)
127
127
  content = Path(fab_path).read_bytes()
128
+
129
+ # Delete FAB file once the bytes is computed
130
+ Path(fab_path).unlink()
131
+
128
132
  fab = Fab(fab_hash, content)
129
133
 
130
134
  # Construct a `ConfigsRecord` out of a flattened `UserConfig`
@@ -138,8 +142,6 @@ def _run_with_exec_api(
138
142
  )
139
143
  res = stub.StartRun(req)
140
144
 
141
- # Delete FAB file once it has been sent to the Exec API
142
- Path(fab_path).unlink()
143
145
  typer.secho(f"🎊 Successfully started run {res.run_id}", fg=typer.colors.GREEN)
144
146
 
145
147
  if stream:
flwr/client/app.py CHANGED
@@ -41,6 +41,7 @@ from flwr.common.constant import (
41
41
  CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
42
42
  ISOLATION_MODE_PROCESS,
43
43
  ISOLATION_MODE_SUBPROCESS,
44
+ MAX_RETRY_DELAY,
44
45
  MISSING_EXTRA_REST,
45
46
  RUN_ID_NUM_BYTES,
46
47
  SERVER_OCTET,
@@ -235,8 +236,6 @@ def start_client_internal(
235
236
  flwr_path: Optional[Path] = None,
236
237
  isolation: Optional[str] = None,
237
238
  clientappio_api_address: Optional[str] = CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
238
- certificates: Optional[tuple[bytes, bytes, bytes]] = None,
239
- ssl_ca_certfile: Optional[str] = None,
240
239
  ) -> None:
241
240
  """Start a Flower client node which connects to a Flower server.
242
241
 
@@ -300,10 +299,6 @@ def start_client_internal(
300
299
  clientappio_api_address : Optional[str]
301
300
  (default: `CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS`)
302
301
  The SuperNode gRPC server address.
303
- certificates : Optional[Tuple[bytes, bytes, bytes]] (default: None)
304
- Tuple containing the CA certificate, server certificate, and server private key.
305
- ssl_ca_certfile : Optional[str] (default: None)
306
- The path to the CA certificate file used by `flwr-clientapp` in subprocess mode.
307
302
  """
308
303
  if insecure is None:
309
304
  insecure = root_certificates is None
@@ -337,7 +332,7 @@ def start_client_internal(
337
332
  )
338
333
  _clientappio_grpc_server, clientappio_servicer = run_clientappio_api_grpc(
339
334
  address=clientappio_api_address,
340
- certificates=certificates,
335
+ certificates=None,
341
336
  )
342
337
  clientappio_api_address = cast(str, clientappio_api_address)
343
338
 
@@ -373,7 +368,7 @@ def start_client_internal(
373
368
  )
374
369
 
375
370
  retry_invoker = RetryInvoker(
376
- wait_gen_factory=exponential,
371
+ wait_gen_factory=lambda: exponential(max_delay=MAX_RETRY_DELAY),
377
372
  recoverable_exceptions=connection_error_type,
378
373
  max_tries=max_retries + 1 if max_retries is not None else None,
379
374
  max_time=max_wait_time,
@@ -551,11 +546,7 @@ def start_client_internal(
551
546
  "--token",
552
547
  str(token),
553
548
  ]
554
- if ssl_ca_certfile:
555
- command.append("--root-certificates")
556
- command.append(ssl_ca_certfile)
557
- else:
558
- command.append("--insecure")
549
+ command.append("--insecure")
559
550
 
560
551
  subprocess.run(
561
552
  command,
@@ -15,6 +15,7 @@
15
15
  """Flower ClientApp process."""
16
16
 
17
17
  import argparse
18
+ import sys
18
19
  import time
19
20
  from logging import DEBUG, ERROR, INFO
20
21
  from typing import Optional
@@ -24,7 +25,7 @@ import grpc
24
25
  from flwr.cli.install import install_from_fab
25
26
  from flwr.client.client_app import ClientApp, LoadClientAppError
26
27
  from flwr.common import Context, Message
27
- from flwr.common.args import add_args_flwr_app_common, try_obtain_root_certificates
28
+ from flwr.common.args import add_args_flwr_app_common
28
29
  from flwr.common.config import get_flwr_dir
29
30
  from flwr.common.constant import CLIENTAPPIO_API_DEFAULT_CLIENT_ADDRESS, ErrorCode
30
31
  from flwr.common.grpc import create_channel
@@ -57,10 +58,15 @@ from .utils import get_load_client_app_fn
57
58
  def flwr_clientapp() -> None:
58
59
  """Run process-isolated Flower ClientApp."""
59
60
  args = _parse_args_run_flwr_clientapp().parse_args()
61
+ if not args.insecure:
62
+ log(
63
+ ERROR,
64
+ "flwr-clientapp does not support TLS yet. "
65
+ "Please use the '--insecure' flag.",
66
+ )
67
+ sys.exit(1)
60
68
 
61
69
  log(INFO, "Starting Flower ClientApp")
62
- certificates = try_obtain_root_certificates(args, args.clientappio_api_address)
63
-
64
70
  log(
65
71
  DEBUG,
66
72
  "Starting isolated `ClientApp` connected to SuperNode's ClientAppIo API at %s "
@@ -73,7 +79,7 @@ def flwr_clientapp() -> None:
73
79
  run_once=(args.token is not None),
74
80
  token=args.token,
75
81
  flwr_dir=args.flwr_dir,
76
- certificates=certificates,
82
+ certificates=None,
77
83
  )
78
84
 
79
85
 
@@ -155,6 +155,11 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
155
155
  ping_thread: Optional[threading.Thread] = None
156
156
  ping_stop_event = threading.Event()
157
157
 
158
+ # Restrict retries to cases where the status code is UNAVAILABLE
159
+ retry_invoker.should_giveup = (
160
+ lambda e: e.code() != grpc.StatusCode.UNAVAILABLE # type: ignore
161
+ )
162
+
158
163
  ###########################################################################
159
164
  # ping/create_node/delete_node/receive/send/get_run functions
160
165
  ###########################################################################
@@ -28,10 +28,7 @@ from cryptography.hazmat.primitives.serialization import (
28
28
  )
29
29
 
30
30
  from flwr.common import EventType, event
31
- from flwr.common.args import (
32
- try_obtain_root_certificates,
33
- try_obtain_server_certificates,
34
- )
31
+ from flwr.common.args import try_obtain_root_certificates
35
32
  from flwr.common.config import parse_config_args
36
33
  from flwr.common.constant import (
37
34
  CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
@@ -78,8 +75,6 @@ def run_supernode() -> None:
78
75
  sys.exit(1)
79
76
 
80
77
  root_certificates = try_obtain_root_certificates(args, args.superlink)
81
- # Obtain certificates for ClientAppIo API server
82
- server_certificates = try_obtain_server_certificates(args, TRANSPORT_TYPE_GRPC_RERE)
83
78
  load_fn = get_load_client_app_fn(
84
79
  default_app_ref="",
85
80
  app_path=None,
@@ -105,8 +100,6 @@ def run_supernode() -> None:
105
100
  flwr_path=args.flwr_dir,
106
101
  isolation=args.isolation,
107
102
  clientappio_api_address=args.clientappio_api_address,
108
- certificates=server_certificates,
109
- ssl_ca_certfile=args.ssl_ca_certfile,
110
103
  )
111
104
 
112
105
  # Graceful shutdown
@@ -235,25 +228,6 @@ def _parse_args_common(parser: argparse.ArgumentParser) -> None:
235
228
  help="Specifies the path to the PEM-encoded root certificate file for "
236
229
  "establishing secure HTTPS connections.",
237
230
  )
238
- parser.add_argument(
239
- "--ssl-certfile",
240
- help="ClientAppIo API server SSL certificate file (as a path str) "
241
- "to create a secure connection.",
242
- type=str,
243
- default=None,
244
- )
245
- parser.add_argument(
246
- "--ssl-keyfile",
247
- help="ClientAppIo API server SSL private key file (as a path str) "
248
- "to create a secure connection.",
249
- type=str,
250
- )
251
- parser.add_argument(
252
- "--ssl-ca-certfile",
253
- help="ClientAppIo API server SSL CA certificate file (as a path str) "
254
- "to create a secure connection.",
255
- type=str,
256
- )
257
231
  parser.add_argument(
258
232
  "--server",
259
233
  default=FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
flwr/common/args.py CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  import argparse
18
18
  import sys
19
- from logging import DEBUG, WARN
19
+ from logging import DEBUG, ERROR, WARN
20
20
  from os.path import isfile
21
21
  from pathlib import Path
22
22
  from typing import Optional
@@ -49,13 +49,6 @@ def add_args_flwr_app_common(parser: argparse.ArgumentParser) -> None:
49
49
  "paths are provided. By default, the server runs with HTTPS enabled. "
50
50
  "Use this flag only if you understand the risks.",
51
51
  )
52
- parser.add_argument(
53
- "--root-certificates",
54
- metavar="ROOT_CERT",
55
- type=str,
56
- help="Specifies the path to the PEM-encoded root certificate file for "
57
- "establishing secure HTTPS connections.",
58
- )
59
52
 
60
53
 
61
54
  def try_obtain_root_certificates(
@@ -80,9 +73,18 @@ def try_obtain_root_certificates(
80
73
  root_certificates = None
81
74
  else:
82
75
  # Load the certificates if provided, or load the system certificates
83
- if not isfile(root_cert_path):
84
- sys.exit("Path argument `--root-certificates` does not point to a file.")
85
- root_certificates = Path(root_cert_path).read_bytes()
76
+ if root_cert_path is None:
77
+ log(
78
+ WARN,
79
+ "Both `--insecure` and `--root-certificates` were not set. "
80
+ "Using system certificates.",
81
+ )
82
+ root_certificates = None
83
+ elif not isfile(root_cert_path):
84
+ log(ERROR, "Path argument `--root-certificates` does not point to a file.")
85
+ sys.exit(1)
86
+ else:
87
+ root_certificates = Path(root_cert_path).read_bytes()
86
88
  log(
87
89
  DEBUG,
88
90
  "Starting secure HTTPS channel to %s "
@@ -140,9 +142,11 @@ def try_obtain_server_certificates(
140
142
  "and `--ssl-keyfile` to create a secure connection "
141
143
  "in Fleet API server (REST, experimental)."
142
144
  )
143
- sys.exit(
145
+ log(
146
+ ERROR,
144
147
  "Certificates are required unless running in insecure mode. "
145
148
  "Please provide certificate paths to `--ssl-certfile`, "
146
149
  "`--ssl-keyfile`, and `—-ssl-ca-certfile` or run the server "
147
- "in insecure mode using '--insecure' if you understand the risks."
150
+ "in insecure mode using '--insecure' if you understand the risks.",
148
151
  )
152
+ sys.exit(1)
flwr/common/constant.py CHANGED
@@ -107,6 +107,9 @@ CONN_RECONNECT_INTERVAL = 0.5 # Reconnect interval between two stream connectio
107
107
  LOG_STREAM_INTERVAL = 0.5 # Log stream interval for `ExecServicer.StreamLogs`
108
108
  LOG_UPLOAD_INTERVAL = 0.2 # Minimum interval between two log uploads
109
109
 
110
+ # Retry configurations
111
+ MAX_RETRY_DELAY = 20 # Maximum delay duration between two consecutive retries.
112
+
110
113
 
111
114
  class MessageType:
112
115
  """Message type."""
flwr/common/grpc.py CHANGED
@@ -53,7 +53,10 @@ def create_channel(
53
53
  channel = grpc.insecure_channel(server_address, options=channel_options)
54
54
  log(DEBUG, "Opened insecure gRPC connection (no certificates were passed)")
55
55
  else:
56
- ssl_channel_credentials = grpc.ssl_channel_credentials(root_certificates)
56
+ try:
57
+ ssl_channel_credentials = grpc.ssl_channel_credentials(root_certificates)
58
+ except Exception as e:
59
+ raise ValueError(f"Failed to create SSL channel credentials: {e}") from e
57
60
  channel = grpc.secure_channel(
58
61
  server_address, ssl_channel_credentials, options=channel_options
59
62
  )
flwr/server/app.py CHANGED
@@ -278,7 +278,7 @@ def run_superlink() -> None:
278
278
  address=simulationio_address,
279
279
  state_factory=state_factory,
280
280
  ffs_factory=ffs_factory,
281
- certificates=certificates,
281
+ certificates=None, # SimulationAppIo API doesn't support SSL yet
282
282
  )
283
283
  grpc_servers.append(simulationio_server)
284
284
 
@@ -288,7 +288,7 @@ def run_superlink() -> None:
288
288
  address=serverappio_address,
289
289
  state_factory=state_factory,
290
290
  ffs_factory=ffs_factory,
291
- certificates=certificates,
291
+ certificates=None, # ServerAppIo API doesn't support SSL yet
292
292
  )
293
293
  grpc_servers.append(serverappio_server)
294
294
 
@@ -389,6 +389,9 @@ def run_superlink() -> None:
389
389
  io_address = (
390
390
  f"{CLIENT_OCTET}:{_port}" if _octet == SERVER_OCTET else serverappio_address
391
391
  )
392
+ address_arg = (
393
+ "--simulationio-api-address" if sim_exec else "--serverappio-api-address"
394
+ )
392
395
  address = simulationio_address if sim_exec else io_address
393
396
  cmd = "flwr-simulation" if sim_exec else "flwr-serverapp"
394
397
 
@@ -397,8 +400,8 @@ def run_superlink() -> None:
397
400
  target=_flwr_scheduler,
398
401
  args=(
399
402
  state_factory,
403
+ address_arg,
400
404
  address,
401
- args.ssl_ca_certfile,
402
405
  cmd,
403
406
  ),
404
407
  )
@@ -423,8 +426,8 @@ def run_superlink() -> None:
423
426
 
424
427
  def _flwr_scheduler(
425
428
  state_factory: LinkStateFactory,
429
+ io_api_arg: str,
426
430
  io_api_address: str,
427
- ssl_ca_certfile: Optional[str],
428
431
  cmd: str,
429
432
  ) -> None:
430
433
  log(DEBUG, "Started %s scheduler thread.", cmd)
@@ -448,14 +451,10 @@ def _flwr_scheduler(
448
451
  command = [
449
452
  cmd,
450
453
  "--run-once",
451
- "--serverappio-api-address",
454
+ io_api_arg,
452
455
  io_api_address,
456
+ "--insecure",
453
457
  ]
454
- if ssl_ca_certfile:
455
- command.append("--root-certificates")
456
- command.append(ssl_ca_certfile)
457
- else:
458
- command.append("--insecure")
459
458
 
460
459
  subprocess.Popen( # pylint: disable=consider-using-with
461
460
  command,
@@ -17,15 +17,16 @@
17
17
  import time
18
18
  import warnings
19
19
  from collections.abc import Iterable
20
- from logging import DEBUG, WARNING
21
- from typing import Optional, cast
20
+ from logging import DEBUG, INFO, WARN, WARNING
21
+ from typing import Any, Optional, cast
22
22
 
23
23
  import grpc
24
24
 
25
25
  from flwr.common import DEFAULT_TTL, Message, Metadata, RecordSet
26
- from flwr.common.constant import SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS
26
+ from flwr.common.constant import MAX_RETRY_DELAY, SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS
27
27
  from flwr.common.grpc import create_channel
28
28
  from flwr.common.logger import log
29
+ from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
29
30
  from flwr.common.serde import message_from_taskres, message_to_taskins, run_from_proto
30
31
  from flwr.common.typing import Run
31
32
  from flwr.proto.node_pb2 import Node # pylint: disable=E0611
@@ -75,6 +76,7 @@ class GrpcDriver(Driver):
75
76
  self._grpc_stub: Optional[ServerAppIoStub] = None
76
77
  self._channel: Optional[grpc.Channel] = None
77
78
  self.node = Node(node_id=0, anonymous=True)
79
+ self._retry_invoker = _make_simple_grpc_retry_invoker()
78
80
 
79
81
  @property
80
82
  def _is_connected(self) -> bool:
@@ -95,6 +97,7 @@ class GrpcDriver(Driver):
95
97
  root_certificates=self._cert,
96
98
  )
97
99
  self._grpc_stub = ServerAppIoStub(self._channel)
100
+ _wrap_stub(self._grpc_stub, self._retry_invoker)
98
101
  log(DEBUG, "[Driver] Connected to %s", self._addr)
99
102
 
100
103
  def _disconnect(self) -> None:
@@ -255,3 +258,60 @@ class GrpcDriver(Driver):
255
258
  return
256
259
  # Disconnect
257
260
  self._disconnect()
261
+
262
+
263
+ def _make_simple_grpc_retry_invoker() -> RetryInvoker:
264
+ """Create a simple gRPC retry invoker."""
265
+
266
+ def _on_sucess(retry_state: RetryState) -> None:
267
+ if retry_state.tries > 1:
268
+ log(
269
+ INFO,
270
+ "Connection successful after %.2f seconds and %s tries.",
271
+ retry_state.elapsed_time,
272
+ retry_state.tries,
273
+ )
274
+
275
+ def _on_backoff(retry_state: RetryState) -> None:
276
+ if retry_state.tries == 1:
277
+ log(WARN, "Connection attempt failed, retrying...")
278
+ else:
279
+ log(
280
+ WARN,
281
+ "Connection attempt failed, retrying in %.2f seconds",
282
+ retry_state.actual_wait,
283
+ )
284
+
285
+ def _on_giveup(retry_state: RetryState) -> None:
286
+ if retry_state.tries > 1:
287
+ log(
288
+ WARN,
289
+ "Giving up reconnection after %.2f seconds and %s tries.",
290
+ retry_state.elapsed_time,
291
+ retry_state.tries,
292
+ )
293
+
294
+ return RetryInvoker(
295
+ wait_gen_factory=lambda: exponential(max_delay=MAX_RETRY_DELAY),
296
+ recoverable_exceptions=grpc.RpcError,
297
+ max_tries=None,
298
+ max_time=None,
299
+ on_success=_on_sucess,
300
+ on_backoff=_on_backoff,
301
+ on_giveup=_on_giveup,
302
+ should_giveup=lambda e: e.code() != grpc.StatusCode.UNAVAILABLE, # type: ignore
303
+ )
304
+
305
+
306
+ def _wrap_stub(stub: ServerAppIoStub, retry_invoker: RetryInvoker) -> None:
307
+ """Wrap the gRPC stub with a retry invoker."""
308
+
309
+ def make_lambda(original_method: Any) -> Any:
310
+ return lambda *args, **kwargs: retry_invoker.invoke(
311
+ original_method, *args, **kwargs
312
+ )
313
+
314
+ for method_name in vars(stub):
315
+ method = getattr(stub, method_name)
316
+ if callable(method):
317
+ setattr(stub, method_name, make_lambda(method))
@@ -15,6 +15,7 @@
15
15
  """Flower ServerApp process."""
16
16
 
17
17
  import argparse
18
+ import sys
18
19
  from logging import DEBUG, ERROR, INFO
19
20
  from pathlib import Path
20
21
  from queue import Queue
@@ -23,7 +24,7 @@ from typing import Optional
23
24
 
24
25
  from flwr.cli.config_utils import get_fab_metadata
25
26
  from flwr.cli.install import install_from_fab
26
- from flwr.common.args import add_args_flwr_app_common, try_obtain_root_certificates
27
+ from flwr.common.args import add_args_flwr_app_common
27
28
  from flwr.common.config import (
28
29
  get_flwr_dir,
29
30
  get_fused_config_from_dir,
@@ -69,7 +70,14 @@ def flwr_serverapp() -> None:
69
70
  args = _parse_args_run_flwr_serverapp().parse_args()
70
71
 
71
72
  log(INFO, "Starting Flower ServerApp")
72
- certificates = try_obtain_root_certificates(args, args.serverappio_api_address)
73
+
74
+ if not args.insecure:
75
+ log(
76
+ ERROR,
77
+ "`flwr-serverapp` does not support TLS yet. "
78
+ "Please use the '--insecure' flag.",
79
+ )
80
+ sys.exit(1)
73
81
 
74
82
  log(
75
83
  DEBUG,
@@ -81,7 +89,7 @@ def flwr_serverapp() -> None:
81
89
  log_queue=log_queue,
82
90
  run_once=args.run_once,
83
91
  flwr_dir=args.flwr_dir,
84
- certificates=certificates,
92
+ certificates=None,
85
93
  )
86
94
 
87
95
  # Restore stdout/stderr
@@ -185,6 +193,12 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
185
193
  run_status = RunStatus(Status.FINISHED, SubStatus.FAILED, str(ex))
186
194
 
187
195
  finally:
196
+ # Stop log uploader for this run and upload final logs
197
+ if log_uploader:
198
+ stop_log_uploader(log_queue, log_uploader)
199
+ log_uploader = None
200
+
201
+ # Update run status
188
202
  if run_status:
189
203
  run_status_proto = run_status_to_proto(run_status)
190
204
  driver._stub.UpdateRunStatus(
@@ -193,11 +207,6 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
193
207
  )
194
208
  )
195
209
 
196
- # Stop log uploader for this run
197
- if log_uploader:
198
- stop_log_uploader(log_queue, log_uploader)
199
- log_uploader = None
200
-
201
210
  # Stop the loop if `flwr-serverapp` is expected to process a single run
202
211
  if run_once:
203
212
  break
flwr/simulation/app.py CHANGED
@@ -14,8 +14,8 @@
14
14
  # ==============================================================================
15
15
  """Flower Simulation process."""
16
16
 
17
-
18
17
  import argparse
18
+ import sys
19
19
  from logging import DEBUG, ERROR, INFO
20
20
  from queue import Queue
21
21
  from time import sleep
@@ -24,7 +24,7 @@ from typing import Optional
24
24
  from flwr.cli.config_utils import get_fab_metadata
25
25
  from flwr.cli.install import install_from_fab
26
26
  from flwr.common import EventType
27
- from flwr.common.args import try_obtain_root_certificates
27
+ from flwr.common.args import add_args_flwr_app_common
28
28
  from flwr.common.config import (
29
29
  get_flwr_dir,
30
30
  get_fused_config_from_dir,
@@ -32,7 +32,11 @@ from flwr.common.config import (
32
32
  get_project_dir,
33
33
  unflatten_dict,
34
34
  )
35
- from flwr.common.constant import Status, SubStatus
35
+ from flwr.common.constant import (
36
+ SIMULATIONIO_API_DEFAULT_CLIENT_ADDRESS,
37
+ Status,
38
+ SubStatus,
39
+ )
36
40
  from flwr.common.logger import (
37
41
  log,
38
42
  mirror_output_to_queue,
@@ -69,61 +73,30 @@ def flwr_simulation() -> None:
69
73
  log_queue: Queue[Optional[str]] = Queue()
70
74
  mirror_output_to_queue(log_queue)
71
75
 
72
- parser = argparse.ArgumentParser(
73
- description="Run a Flower Simulation",
74
- )
75
- parser.add_argument(
76
- "--superlink",
77
- type=str,
78
- help="Address of SuperLink's SimulationIO API",
79
- )
80
- parser.add_argument(
81
- "--run-once",
82
- action="store_true",
83
- help="When set, this process will start a single simulation "
84
- "for a pending Run. If no pending run the process will exit. ",
85
- )
86
- parser.add_argument(
87
- "--flwr-dir",
88
- default=None,
89
- help="""The path containing installed Flower Apps.
90
- By default, this value is equal to:
91
-
92
- - `$FLWR_HOME/` if `$FLWR_HOME` is defined
93
- - `$XDG_DATA_HOME/.flwr/` if `$XDG_DATA_HOME` is defined
94
- - `$HOME/.flwr/` in all other cases
95
- """,
96
- )
97
- parser.add_argument(
98
- "--insecure",
99
- action="store_true",
100
- help="Run the server without HTTPS, regardless of whether certificate "
101
- "paths are provided. By default, the server runs with HTTPS enabled. "
102
- "Use this flag only if you understand the risks.",
103
- )
104
- parser.add_argument(
105
- "--root-certificates",
106
- metavar="ROOT_CERT",
107
- type=str,
108
- help="Specifies the path to the PEM-encoded root certificate file for "
109
- "establishing secure HTTPS connections.",
110
- )
111
- args = parser.parse_args()
76
+ args = _parse_args_run_flwr_simulation().parse_args()
112
77
 
113
78
  log(INFO, "Starting Flower Simulation")
114
- certificates = try_obtain_root_certificates(args, args.superlink)
79
+
80
+ if not args.insecure:
81
+ log(
82
+ ERROR,
83
+ "`flwr-simulation` does not support TLS yet. "
84
+ "Please use the '--insecure' flag.",
85
+ )
86
+ sys.exit(1)
115
87
 
116
88
  log(
117
89
  DEBUG,
118
- "Staring isolated `Simulation` connected to SuperLink DriverAPI at %s",
119
- args.superlink,
90
+ "Starting isolated `Simulation` connected to SuperLink SimulationAppIo API "
91
+ "at %s",
92
+ args.simulationio_api_address,
120
93
  )
121
94
  run_simulation_process(
122
- simulationio_api_address=args.superlink,
95
+ simulationio_api_address=args.simulationio_api_address,
123
96
  log_queue=log_queue,
124
97
  run_once=args.run_once,
125
98
  flwr_dir_=args.flwr_dir,
126
- certificates=certificates,
99
+ certificates=None,
127
100
  )
128
101
 
129
102
  # Restore stdout/stderr
@@ -225,7 +198,7 @@ def run_simulation_process( # pylint: disable=R0914, disable=W0212, disable=R09
225
198
  )
226
199
  backend_config: BackendConfig = fed_opt.get("backend", {})
227
200
  verbose: bool = fed_opt.get("verbose", False)
228
- enable_tf_gpu_growth: bool = fed_opt.get("enable_tf_gpu_growth", True)
201
+ enable_tf_gpu_growth: bool = fed_opt.get("enable_tf_gpu_growth", False)
229
202
 
230
203
  # Launch the simulation
231
204
  _run_simulation(
@@ -257,6 +230,12 @@ def run_simulation_process( # pylint: disable=R0914, disable=W0212, disable=R09
257
230
  run_status = RunStatus(Status.FINISHED, SubStatus.FAILED, str(ex))
258
231
 
259
232
  finally:
233
+ # Stop log uploader for this run and upload final logs
234
+ if log_uploader:
235
+ stop_log_uploader(log_queue, log_uploader)
236
+ log_uploader = None
237
+
238
+ # Update run status
260
239
  if run_status:
261
240
  run_status_proto = run_status_to_proto(run_status)
262
241
  conn._stub.UpdateRunStatus(
@@ -265,11 +244,28 @@ def run_simulation_process( # pylint: disable=R0914, disable=W0212, disable=R09
265
244
  )
266
245
  )
267
246
 
268
- # Stop log uploader for this run
269
- if log_uploader:
270
- stop_log_uploader(log_queue, log_uploader)
271
- log_uploader = None
272
-
273
247
  # Stop the loop if `flwr-simulation` is expected to process a single run
274
248
  if run_once:
275
249
  break
250
+
251
+
252
+ def _parse_args_run_flwr_simulation() -> argparse.ArgumentParser:
253
+ """Parse flwr-simulation command line arguments."""
254
+ parser = argparse.ArgumentParser(
255
+ description="Run a Flower Simulation",
256
+ )
257
+ parser.add_argument(
258
+ "--simulationio-api-address",
259
+ default=SIMULATIONIO_API_DEFAULT_CLIENT_ADDRESS,
260
+ type=str,
261
+ help="Address of SuperLink's SimulationIO API (IPv4, IPv6, or a domain name)."
262
+ f"By default, it is set to {SIMULATIONIO_API_DEFAULT_CLIENT_ADDRESS}.",
263
+ )
264
+ parser.add_argument(
265
+ "--run-once",
266
+ action="store_true",
267
+ help="When set, this process will start a single simulation "
268
+ "for a pending Run. If no pending run the process will exit. ",
269
+ )
270
+ add_args_flwr_app_common(parser=parser)
271
+ return parser
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.13.0.dev20241117
3
+ Version: 1.14.0.dev20241126
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -34,7 +34,6 @@ Provides-Extra: rest
34
34
  Provides-Extra: simulation
35
35
  Requires-Dist: cryptography (>=42.0.4,<43.0.0)
36
36
  Requires-Dist: grpcio (>=1.60.0,<2.0.0,!=1.64.2,<=1.64.3)
37
- Requires-Dist: hatchling (>=1.25.0,<2.0.0)
38
37
  Requires-Dist: iterators (>=0.0.2,<0.0.3)
39
38
  Requires-Dist: numpy (>=1.26.0,<3.0.0)
40
39
  Requires-Dist: pathspec (>=0.12.1,<0.13.0)
@@ -1,20 +1,20 @@
1
1
  flwr/__init__.py,sha256=VmBWedrCxqmt4QvUHBLqyVEH6p7zaFMD_oCHerXHSVw,937
2
2
  flwr/cli/__init__.py,sha256=cZJVgozlkC6Ni2Hd_FAIrqefrkCGOV18fikToq-6iLw,720
3
3
  flwr/cli/app.py,sha256=h1RCGBUtiL1JTC6yoihlOlB-k4imJ0ToS8uG4haFiyU,1292
4
- flwr/cli/build.py,sha256=a_43l2HL2K1z2x_IJGUw_4KrEZrJiiw_yVq_bdUyFes,7457
4
+ flwr/cli/build.py,sha256=k2M0aIY2q5WB_yXQ22Woxt1zb6m-Z1wNwmhWMxEm5Dw,6344
5
5
  flwr/cli/config_utils.py,sha256=n-xNkQG_0POz5UUHyE00lthNaOjuS6IYU9Thzb_BThs,11431
6
6
  flwr/cli/example.py,sha256=1bGDYll3BXQY2kRqSN-oICqS5n1b9m0g0RvXTopXHl4,2215
7
- flwr/cli/install.py,sha256=kDLZDZpIVIK9GaNdxCEHHAEAfqo5IfdG2P2SRZHjSDI,8695
7
+ flwr/cli/install.py,sha256=kmD2dW-9B7645GAQx5es1o2W11gRHQ2Fg2E31SLomrg,8179
8
8
  flwr/cli/log.py,sha256=WlAuxZdTUYZ5bRKkm0jLWrOxHTS0TlSA5BeDtO9xF3k,6659
9
- flwr/cli/ls.py,sha256=lUXG07ebrq0bYOWdZk9i8pslvmou_v6DNiuj_PugOqY,7704
9
+ flwr/cli/ls.py,sha256=aOQTDaRMYuh8O7Wm1CjbSvEXhwq-25QRRDbq-v5UaXY,7703
10
10
  flwr/cli/new/__init__.py,sha256=cQzK1WH4JP2awef1t2UQ2xjl1agVEz9rwutV18SWV1k,789
11
- flwr/cli/new/new.py,sha256=uSiG7aXQzPDnikv2YcjQ86OOLqint0hNWCI0fSQD0jI,9634
11
+ flwr/cli/new/new.py,sha256=xgzObnhNpnGvjVs6wTj6BlJ9X-avPhRX3DuwWnk9ED0,9903
12
12
  flwr/cli/new/templates/__init__.py,sha256=4luU8RL-CK8JJCstQ_ON809W9bNTkY1l9zSaPKBkgwY,725
13
13
  flwr/cli/new/templates/app/.gitignore.tpl,sha256=XixnHdyeMB2vwkGtGnwHqoWpH-9WChdyG0GXe57duhc,3078
14
14
  flwr/cli/new/templates/app/LICENSE.tpl,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
15
15
  flwr/cli/new/templates/app/README.baseline.md.tpl,sha256=4dg2aBS-NIleVyDlxsG8m65Af6LIJ-pZA5ICjGFU5XA,9641
16
16
  flwr/cli/new/templates/app/README.flowertune.md.tpl,sha256=QSG51uifue2KVZz2ZNw8kmOStS7svC2AQ2gTa5E7Bhs,3326
17
- flwr/cli/new/templates/app/README.md.tpl,sha256=t7w4YFZEcJOxAnuJmNPw5-fDdIJu7PfLd8gFJDiBwwo,436
17
+ flwr/cli/new/templates/app/README.md.tpl,sha256=6HBXeD0PBLAnx1IYWGsXzLmDn52jPEk0SF24omhhczE,949
18
18
  flwr/cli/new/templates/app/__init__.py,sha256=DU7QMY7IhMQyuwm_tja66xU0KXTWQFqzfTqwg-_NJdE,729
19
19
  flwr/cli/new/templates/app/code/__init__.baseline.py.tpl,sha256=YkHAgppUeD2BnBoGfVB6dEvBfjuIPGsU1gw4CiUi3qA,40
20
20
  flwr/cli/new/templates/app/code/__init__.py,sha256=EM6vfvgAILKPaPn7H1wMV1Wi01WyZCP_Eg6NxD6oWg8,736
@@ -52,24 +52,24 @@ flwr/cli/new/templates/app/code/task.pytorch.py.tpl,sha256=XlJqA4Ix_PloO_zJLhjiN
52
52
  flwr/cli/new/templates/app/code/task.sklearn.py.tpl,sha256=SeIIo0rr_6ffn4Qx2xELD18jYXCkcW__NWtYEDXCICM,1843
53
53
  flwr/cli/new/templates/app/code/task.tensorflow.py.tpl,sha256=SKXAZdgBnPpbAbJ90Rb7oQ5ilnopBx_j_JNFoUDeEAI,1732
54
54
  flwr/cli/new/templates/app/code/utils.baseline.py.tpl,sha256=YkHAgppUeD2BnBoGfVB6dEvBfjuIPGsU1gw4CiUi3qA,40
55
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl,sha256=8zSefvfRhuVo_ZLEDbIlLMQJa-dr2iIRhAgqwaaKMYk,2666
56
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl,sha256=erfZhYcdU1KAXG4AVXCMP2pUMdZI8mcgijArJDrpwIs,1873
57
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl,sha256=foFjtZ8lI67RLAMxShdoph29i9IAT4jS0lW1wH3sWFQ,1143
58
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl,sha256=hna-BiIVZvUtTb8-ptaGAHBXJrvVPqnh44NYt4dA_hk,673
59
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl,sha256=GiRhllAQnMfH3mP4Cr9qpa1qvGwzbcULCa-QDVEbLOY,765
60
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl,sha256=89B4qMlOI4MrRyzKRzB2ORHJIkyvGx7IeoFnY_TAfVc,612
61
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=LUsQsEVhCgGzKEBB5IewKjZOKtcsq9fUQW-7_NJpgwk,710
62
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=BiZPOKEoGiZOuxSHQY_nQHj3KH7wH7QAiVmpxGutOgk,686
63
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=6R_bIGyPvXlCtLABF8fHLmGBNlIptG8QrYM69_Fy_nk,710
55
+ flwr/cli/new/templates/app/pyproject.baseline.toml.tpl,sha256=su1WcJ0KkWar8op-6QB03gw4NVu6AxTcjAR2PdbPGiw,2666
56
+ flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl,sha256=TkzINYGgDHi-hJJKF8XGGIOUcFaxj09LEEsibLKs0jY,1873
57
+ flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl,sha256=GndNKPk2j63HmcoDb0PkUuLWajMHw5fD28q18a7EtiU,1143
58
+ flwr/cli/new/templates/app/pyproject.jax.toml.tpl,sha256=1kauOcIjXJWZT0ONXYxPZpO1qdnjK_2c1S-Fymxzy54,673
59
+ flwr/cli/new/templates/app/pyproject.mlx.toml.tpl,sha256=pzsL4mG5WxbgHa5JZgJN_ty3MkWb4OZBs7qTr1xmMgw,765
60
+ flwr/cli/new/templates/app/pyproject.numpy.toml.tpl,sha256=qMu1YXahxEqslcGyiGoG6uSzBX8XkH4Vp36cg4-K8aA,612
61
+ flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=Fgw-KjFS7F9y9YDzrfXrx5gyp8_2t_TlaThT3Ug9ow4,710
62
+ flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=TcaTNCrJY98V-CcgzHiN_Wx4FeleV24Idh0AeJrM7ec,686
63
+ flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=ObRYYJhgeOWMmBasda1x0UOIhwZKpTtaNon9jazmgOI,710
64
64
  flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
65
- flwr/cli/run/run.py,sha256=TQm7M0a-oSeNXPCsn75qYrkGB0IPbk0HulcYk1gwmR0,6293
65
+ flwr/cli/run/run.py,sha256=t4fBBc3Fq_-Ay5KA8zfkil5NVQOOJOMIN1Nto7Suwy4,6284
66
66
  flwr/cli/utils.py,sha256=emMUdthvoHBTB0iGQp-oFBmA5wV46lw3y3FmfXQPCsc,4500
67
67
  flwr/client/__init__.py,sha256=DGDoO0AEAfz-0CUFmLdyUUweAS64-07AOnmDfWUefK4,1192
68
- flwr/client/app.py,sha256=1XroQmYNXSoTTVIWpKcCqkYYgnBMMVNtB6fPrZmd3ok,34980
68
+ flwr/client/app.py,sha256=3AKrJduvki_1ATvKCQV4T9_1qZuarVVTtpnsq6_cWw0,34384
69
69
  flwr/client/client.py,sha256=gy6WVlMUFAp8oevN4xpQPX30vPOIYGVqdbuFlTWkyG4,9080
70
70
  flwr/client/client_app.py,sha256=cTig-N00YzTucbo9zNi6I21J8PlbflU_8J_f5CI-Wpw,10390
71
71
  flwr/client/clientapp/__init__.py,sha256=kZqChGnTChQ1WGSUkIlW2S5bc0d0mzDubCAmZUGRpEY,800
72
- flwr/client/clientapp/app.py,sha256=W6wOlysris-JGKM5Wq-OrYNHl7rYgaAepEaRI1wMWo4,8725
72
+ flwr/client/clientapp/app.py,sha256=BQJMhCgkj72bfzpinJjJGRlFlZKYuIcZEGrtkfU_os0,8806
73
73
  flwr/client/clientapp/clientappio_servicer.py,sha256=5L6bjw_j3Mnx9kRFwYwxDNABKurBO5q1jZOWE_X11wQ,8522
74
74
  flwr/client/clientapp/utils.py,sha256=Xg23Q7g7r9jrxXEbvJ9wXal_uAqYK3mi087u0QER6-I,4343
75
75
  flwr/client/dpfedavg_numpy_client.py,sha256=4KsEvzavDKyVDU1V0kMqffTwu1lNdUCYQN-i0DTYVN8,7404
@@ -79,7 +79,7 @@ flwr/client/grpc_client/__init__.py,sha256=LsnbqXiJhgQcB0XzAlUQgPx011Uf7Y7yabIC1
79
79
  flwr/client/grpc_client/connection.py,sha256=Y9yJkRvGypulerX9fviP4axF5XBTBnvYrsp2mv5OS9E,9411
80
80
  flwr/client/grpc_rere_client/__init__.py,sha256=MK-oSoV3kwUEQnIwl0GN4OpiHR7eLOrMA8ikunET130,752
81
81
  flwr/client/grpc_rere_client/client_interceptor.py,sha256=q08lIEeJLvvonNOiejNXvmySbPObteGnbDHhEKDmWzE,5380
82
- flwr/client/grpc_rere_client/connection.py,sha256=c3s-79EToJymlCDNu5aIwu3qZ9XW8DMNGXoOG6fR52E,10929
82
+ flwr/client/grpc_rere_client/connection.py,sha256=1qJB9XAlOl8m2MrvujAjo8y6WGu10IK2rkLXg_sj7BQ,11115
83
83
  flwr/client/grpc_rere_client/grpc_adapter.py,sha256=sQo0I9T65t97LFGoW_PrmgaTbd18GFgi2DoAI5wQJ4k,5589
84
84
  flwr/client/heartbeat.py,sha256=cx37mJBH8LyoIN4Lks85wtqT1mnU5GulQnr4pGCvAq0,2404
85
85
  flwr/client/message_handler/__init__.py,sha256=QxxQuBNpFPTHx3KiUNvQSlqMKlEnbRR1kFfc1KVje08,719
@@ -102,20 +102,20 @@ flwr/client/rest_client/__init__.py,sha256=5KGlp7pjc1dhNRkKlaNtUfQmg8wrRFh9lS3P3
102
102
  flwr/client/rest_client/connection.py,sha256=cBLtI-nUTELn5zpS42sbnc--0wxc1DdHkVGAlfRSc2Y,12626
103
103
  flwr/client/run_info_store.py,sha256=ZN2Phi4DSLbSyzg8RmzJcVYh1g6eurHOmWRCT7GMtw4,4040
104
104
  flwr/client/supernode/__init__.py,sha256=SUhWOzcgXRNXk1V9UgB5-FaWukqqrOEajVUHEcPkwyQ,865
105
- flwr/client/supernode/app.py,sha256=gb7tkT3l6tF2hOumeaELDQFvnTHwtvt5rSELVXUsTdo,12363
105
+ flwr/client/supernode/app.py,sha256=CWVSrrZ6YbQ1PcoXQqWZiYKjKcwcE3bB-4Wl-oIG5co,11482
106
106
  flwr/client/typing.py,sha256=dxoTBnTMfqXr5J7G3y-uNjqxYCddvxhu89spfj4Lm2U,1048
107
107
  flwr/common/__init__.py,sha256=TVaoFEJE158aui1TPZQiJCDZX4RNHRyI8I55VC80HhI,3901
108
108
  flwr/common/address.py,sha256=7kM2Rqjw86-c8aKwAvrXerWqznnVv4TFJ62aSAeTn10,3017
109
- flwr/common/args.py,sha256=y9qtPIwyijegNi0GbQT5ddRtMUITM-DlgilC1Co-nvw,6133
109
+ flwr/common/args.py,sha256=-KeQ6AZw1-G4Ifhsg4qlRnWhGH1m_OzUgxH7Z4j_0ns,6222
110
110
  flwr/common/config.py,sha256=qC1QvGAGr4faBtg3Y5dWhfyK5FggyWUMjPqg-Rx_FW4,8083
111
- flwr/common/constant.py,sha256=41xaDQdeDMowFQ3Y34V132ICftGJJ2cVrZYyBmu6FZg,5418
111
+ flwr/common/constant.py,sha256=f8P1oB5VqqvV14ymdlOFDhPNuuy6TWtiG0CI7lFw-F8,5522
112
112
  flwr/common/context.py,sha256=uJ-mnoC_8y_udEb3kAX-r8CPphNTWM72z1AlsvQEu54,2403
113
113
  flwr/common/date.py,sha256=NHHpESce5wYqEwoDXf09gp9U9l_5Bmlh2BsOcwS-kDM,1554
114
114
  flwr/common/differential_privacy.py,sha256=XwcJ3rWr8S8BZUocc76vLSJAXIf6OHnWkBV6-xlIRuw,6106
115
115
  flwr/common/differential_privacy_constants.py,sha256=c7b7tqgvT7yMK0XN9ndiTBs4mQf6d3qk6K7KBZGlV4Q,1074
116
116
  flwr/common/dp.py,sha256=vddkvyjV2FhRoN4VuU2LeAM1UBn7dQB8_W-Qdiveal8,1978
117
117
  flwr/common/exit_handlers.py,sha256=MracJaBeoCOC7TaXK9zCJQxhrMSx9ZtczK237qvhBpU,2806
118
- flwr/common/grpc.py,sha256=6Yi28JjAll19nxYJlOT9B03RN8dvJZP9zUoR3RSmxoY,2487
118
+ flwr/common/grpc.py,sha256=AIPMAHsvcTlduaYKCgnoBnst1A7RZEgGqh0Ulm7qfJ0,2621
119
119
  flwr/common/logger.py,sha256=q_PKjfgUEH-yrjPSIvzzaKocWBqEOaILAmfKOOyEcDE,11197
120
120
  flwr/common/message.py,sha256=4O1m0OWXBAYZz05gKgEtnoJ94J1gjo7hCNHyUXThxRo,13831
121
121
  flwr/common/object_ref.py,sha256=DavEkh-IJv_s0VeLsJvSZS5k-Ix_k1UcNXbldfNFXxM,9859
@@ -213,7 +213,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
213
213
  flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
214
214
  flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
215
215
  flwr/server/__init__.py,sha256=cEg1oecBu4cKB69iJCqWEylC8b5XW47bl7rQiJsdTvM,1528
216
- flwr/server/app.py,sha256=UAynn_AmJotTMS8ebY1Gqe7Qft9slSlSMKyWfnriB7M,28919
216
+ flwr/server/app.py,sha256=Ah9tYbRIPI88xim29LS7F_PkrUNgRKx4v6Mggy50dE4,28905
217
217
  flwr/server/client_manager.py,sha256=7Ese0tgrH-i-ms363feYZJKwB8gWnXSmg_hYF2Bju4U,6227
218
218
  flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2399
219
219
  flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
@@ -224,7 +224,7 @@ flwr/server/compat/legacy_context.py,sha256=wBzBcfV6YO6IQGriM_FdJ5XZfiBBEEJdS_Od
224
224
  flwr/server/criterion.py,sha256=ypbAexbztzGUxNen9RCHF91QeqiEQix4t4Ih3E-42MM,1061
225
225
  flwr/server/driver/__init__.py,sha256=bikRv6CjTwSvYh7tf10gziU5o2YotOWhhftz2tr3KDc,886
226
226
  flwr/server/driver/driver.py,sha256=u_fMfqLYTroTafGCNwKPHI4lttRL-Z5CqeT3_FHSq-Q,5701
227
- flwr/server/driver/grpc_driver.py,sha256=lIOeAPvTIRAebRitTONZE1ji4UTGjIsMor-dW_X8eDw,9300
227
+ flwr/server/driver/grpc_driver.py,sha256=aTeQVYjyp19LGUa-a5iKdQRFijLzut2bXj1h8YovdIM,11397
228
228
  flwr/server/driver/inmemory_driver.py,sha256=gfB4jmkk1indhRa9XCdKCXghVcWBF1qBD-tAxMUyQm0,6404
229
229
  flwr/server/history.py,sha256=qSb5_pPTrwofpSYGsZWzMPkl_4uJ4mJFWesxXDrEvDU,5026
230
230
  flwr/server/run_serverapp.py,sha256=oDfHaHyVT5BRcckFFQKg8AVPCWR1ek7OhNceTC8qq9g,2493
@@ -232,7 +232,7 @@ flwr/server/server.py,sha256=1ZsFEptmAV-L2vP2etNC9Ed5CLSxpuKzUFkAPQ4l5Xc,17893
232
232
  flwr/server/server_app.py,sha256=RsgS6PRS5Z74cMUAHzsm8r3LWddwn00MjRs6rlacHt8,6297
233
233
  flwr/server/server_config.py,sha256=CZaHVAsMvGLjpWVcLPkiYxgJN4xfIyAiUrCI3fETKY4,1349
234
234
  flwr/server/serverapp/__init__.py,sha256=L0K-94UDdTyEZ8LDtYybGIIIv3HW6AhSVjXMUfYJQnQ,800
235
- flwr/server/serverapp/app.py,sha256=Fa26GCuq53cJYH7cII8iaEq8EtfLrNB90MOeHzU81EE,7554
235
+ flwr/server/serverapp/app.py,sha256=wYs5H8TU7egSB32DzDnJCUHmcli201e7MlWmhkOgn30,7693
236
236
  flwr/server/serverapp_components.py,sha256=-IV_CitOfrJclJj2jNdbN1Q65PyFmtKtrTIg1hc6WQw,2118
237
237
  flwr/server/strategy/__init__.py,sha256=tQer2SwjDnvgFFuJMZM-S01Z615N5XK6MaCvpm4BMU0,2836
238
238
  flwr/server/strategy/aggregate.py,sha256=PDvekufza13s9AsVmz9WASunaBs3yCtl8JVliFx9j6Q,13978
@@ -306,7 +306,7 @@ flwr/server/workflow/secure_aggregation/__init__.py,sha256=3XlgDOjD_hcukTGl6Bc1B
306
306
  flwr/server/workflow/secure_aggregation/secagg_workflow.py,sha256=l2IdMdJjs1bgHs5vQgLSOVzar7v2oxUn46oCrnVE1rM,5839
307
307
  flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=rfn2etO1nb7u-1oRl-H9q3enJZz3shMINZaBB7rPsC4,29671
308
308
  flwr/simulation/__init__.py,sha256=5UcDVJNjFoSwWqHbGM1hKfTTUUNdwAtuoNvNrfvdkUY,1556
309
- flwr/simulation/app.py,sha256=EGF20e-Uv7slJCRQeDI2erjFDiljE_RRo-4TFHOMouk,9576
309
+ flwr/simulation/app.py,sha256=IKUVaO3hQf6KOfvxCXKRNYJIF_Pwz2YPF2jzyMS_zcU,9379
310
310
  flwr/simulation/legacy_app.py,sha256=ySggtKEtXe8L77n8qyGXDA7UPv840MXh-QoalzoGiLU,15780
311
311
  flwr/simulation/ray_transport/__init__.py,sha256=wzcEEwUUlulnXsg6raCA1nGpP3LlAQDtJ8zNkCXcVbA,734
312
312
  flwr/simulation/ray_transport/ray_actor.py,sha256=9-XBguAm5IFqm2ddPFsQtnuuFN6lzqdb00SnCxGUGBo,18996
@@ -321,8 +321,8 @@ flwr/superexec/exec_grpc.py,sha256=OuhBAk7hiky9rjGceinLGIXqchtzGPQThZnwyYv6Ei0,2
321
321
  flwr/superexec/exec_servicer.py,sha256=M3R3q5rg2kYz-gFN-nmiXkvjels4QbieEA0K5wks0kQ,4972
322
322
  flwr/superexec/executor.py,sha256=zH3_53il6Jh0ZscIVEB9f4GNnXMeBbCGyCoBCxLgiG0,3114
323
323
  flwr/superexec/simulation.py,sha256=WQDon15oqpMopAZnwRZoTICYCfHqtkvFSqiTQ2hLD_g,4088
324
- flwr_nightly-1.13.0.dev20241117.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
325
- flwr_nightly-1.13.0.dev20241117.dist-info/METADATA,sha256=hY_iiu43zt7NGG-fsKZBRMdp4o4BCNOx_W0at0f_o9w,15722
326
- flwr_nightly-1.13.0.dev20241117.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
327
- flwr_nightly-1.13.0.dev20241117.dist-info/entry_points.txt,sha256=JlNxX3qhaV18_2yj5a3kJW1ESxm31cal9iS_N_pf1Rk,538
328
- flwr_nightly-1.13.0.dev20241117.dist-info/RECORD,,
324
+ flwr_nightly-1.14.0.dev20241126.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
325
+ flwr_nightly-1.14.0.dev20241126.dist-info/METADATA,sha256=W12Hf5W8vZkLN1Ss9yckjRrIDXRTfxaBlUxaR_0O8_s,15679
326
+ flwr_nightly-1.14.0.dev20241126.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
327
+ flwr_nightly-1.14.0.dev20241126.dist-info/entry_points.txt,sha256=JlNxX3qhaV18_2yj5a3kJW1ESxm31cal9iS_N_pf1Rk,538
328
+ flwr_nightly-1.14.0.dev20241126.dist-info/RECORD,,