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.
- flwr/cli/build.py +0 -37
- flwr/cli/install.py +1 -19
- flwr/cli/ls.py +1 -1
- flwr/cli/new/new.py +23 -13
- flwr/cli/new/templates/app/README.md.tpl +11 -0
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/run/run.py +4 -2
- flwr/client/app.py +4 -13
- flwr/client/clientapp/app.py +10 -4
- flwr/client/grpc_rere_client/connection.py +5 -0
- flwr/client/supernode/app.py +1 -27
- flwr/common/args.py +17 -13
- flwr/common/constant.py +3 -0
- flwr/common/grpc.py +4 -1
- flwr/server/app.py +9 -10
- flwr/server/driver/grpc_driver.py +63 -3
- flwr/server/serverapp/app.py +17 -8
- flwr/simulation/app.py +50 -54
- {flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/METADATA +1 -2
- {flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/RECORD +31 -31
- {flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/WHEEL +0 -0
- {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
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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/)
|
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=
|
|
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
|
-
|
|
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,
|
flwr/client/clientapp/app.py
CHANGED
|
@@ -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
|
|
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=
|
|
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
|
###########################################################################
|
flwr/client/supernode/app.py
CHANGED
|
@@ -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
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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=
|
|
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=
|
|
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
|
-
|
|
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))
|
flwr/server/serverapp/app.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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=
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
119
|
-
|
|
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.
|
|
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=
|
|
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",
|
|
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
|
{flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flwr-nightly
|
|
3
|
-
Version: 1.
|
|
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)
|
{flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/RECORD
RENAMED
|
@@ -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=
|
|
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=
|
|
7
|
+
flwr/cli/install.py,sha256=kmD2dW-9B7645GAQx5es1o2W11gRHQ2Fg2E31SLomrg,8179
|
|
8
8
|
flwr/cli/log.py,sha256=WlAuxZdTUYZ5bRKkm0jLWrOxHTS0TlSA5BeDtO9xF3k,6659
|
|
9
|
-
flwr/cli/ls.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
56
|
-
flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl,sha256=
|
|
57
|
-
flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl,sha256=
|
|
58
|
-
flwr/cli/new/templates/app/pyproject.jax.toml.tpl,sha256=
|
|
59
|
-
flwr/cli/new/templates/app/pyproject.mlx.toml.tpl,sha256=
|
|
60
|
-
flwr/cli/new/templates/app/pyproject.numpy.toml.tpl,sha256=
|
|
61
|
-
flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=
|
|
62
|
-
flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=
|
|
63
|
-
flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
325
|
-
flwr_nightly-1.
|
|
326
|
-
flwr_nightly-1.
|
|
327
|
-
flwr_nightly-1.
|
|
328
|
-
flwr_nightly-1.
|
|
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,,
|
{flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/LICENSE
RENAMED
|
File without changes
|
{flwr_nightly-1.13.0.dev20241117.dist-info → flwr_nightly-1.14.0.dev20241126.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|